好的,所以我做了更多的挖掘工作。
MenuItem 的控件模板甚至没有内部 CheckBox 控件。 MenuItem 使用它自己的 IsChecked 属性,它显示或隐藏一个内部 Path 控件来指示它的状态。
所以,我更改了默认控制模板。我用 CheckBox 替换了 Path,并适当地重新连接了触发器:
<ControlTemplate x:Key="CustomMenuItemControlTemplate" TargetType="{x:Type MenuItem}">
<Grid SnapsToDevicePixels="True" MinWidth="225" MinHeight="26">
<Rectangle x:Name="OuterBorder" RadiusY="2" RadiusX="2"/>
<Rectangle x:Name="Bg" Fill="{TemplateBinding Background}" Margin="1" RadiusY="1" RadiusX="1" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="1"/>
<Rectangle x:Name="InnerBorder" Margin="2"/>
<DockPanel>
<ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" Margin="4,0,6,0" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"/>
<CheckBox x:Name="MenuCheckBox" Margin="7,4" Visibility="Hidden" VerticalAlignment="Center" IsThreeState="True" />
<ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" />
</DockPanel>
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" HorizontalOffset="1" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom" VerticalOffset="-1">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent">
<Border x:Name="SubMenuBorder" BorderBrush="#FF959595" BorderThickness="1" Background="WhiteSmoke">
<ScrollViewer x:Name="SubMenuScrollViewer" Margin="1,0" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="WhiteSmoke" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<Rectangle Fill="#FFF1F1F1" HorizontalAlignment="Left" Margin="1,2" RadiusY="2" RadiusX="2" Width="28"/>
<Rectangle Fill="#FFE2E3E3" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
<Rectangle Fill="White" HorizontalAlignment="Left" Margin="30,2,0,2" Width="1"/>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" Margin="2" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation" Value="True">
<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
</Trigger>
<Trigger Property="Icon" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsCheckable" Value="True">
<Setter Property="Visibility" TargetName="MenuCheckBox" Value="Visible"/>
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="True">
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Fill" TargetName="Bg">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#0462c9f5" Offset="0"/>
<GradientStop Color="#1C62c9f5" Offset="0.75"/>
<GradientStop Color="#3062c9f5" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Stroke" TargetName="OuterBorder" Value="#C062c9f5"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="#FF9A9A9A"/>
</Trigger>
<Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
我在它自己的资源字典中有这个。您必须在项目中添加对“PresentationFramework.Aero”和以下 xmlns 的引用:
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
然后,我将它们全部连接成一个样式:
<Style TargetType="MenuItem">
<Style.Resources>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="IsChecked" Value="{Binding IsSelected}" />
</Style>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource CustomMenuItemControlTemplate}" />
<Setter Property="IsCheckable" Value="True" />
<Setter Property="IsChecked" Value="{Binding ToggleIsSelected}" />
<Setter Property="StaysOpenOnClick" Value="True" />
</Style>
拼图的最后一块是绑定。我现在有两个属性:
1) IsSelected - 我绑定到内部 CheckBox 的 IsChecked 属性的 bool?
2) ToggleIsSelected - 我绑定到 MenuItem 的 IsChecked 属性的 bool
拥有这两个绑定允许用户单击 MenuItem 中的任意位置来切换 CheckBox,如果 IsSelected 为空,则不会出现任何绑定错误。属性定义为:
public bool? IsSelected
{
get
{
return _isSelected;
}
set
{
if (value == _isSelected)
return;
_isSelected = value ?? false;
OnPropertyChanged();
OnPropertyChanged("ToggleIsSelected");
}
}
和
public bool ToggleIsSelected
{
get
{
return IsSelected ?? false;
}
set
{
if (value == IsSelected)
return;
IsSelected = value;
}
}
这样做的一个潜在缺点是用户无法将 CheckBox 切换到它的 null 状态。就我而言,我只想编程将其设置为空,所以它对我有用。如果您需要用户切换为 null,请从您的 ViewModel 中删除 ToggleIsSelected 属性以及相关的 setter:
<Setter Property="IsChecked" Value="{Binding ToggleIsSelected}" />
...并将 IsSelected 属性更改为:
public bool? IsSelected
{
get
{
return _isSelected;
}
set
{
if (value == _isSelected)
return;
_isSelected = value;
OnPropertyChanged();
}
}
这也将取消单击任意位置进行切换的功能,因此您的用户将不得不直接检查 CheckBox...除非您想出其他方法来实现它。
我知道这是一个很长的答案......但我认为其他人可能需要这个。如果可以的话,还不如为他们节省时间。如果您发现任何改进空间,请告诉我。