如何在WPF MenuItem复选框中设置IsThreeState = True?

时间:2015-04-23 18:27:25

标签: c# wpf menuitem

我希望有一个MenuItem,其内部CheckBox具有属性" IsThreeState"设为true。我想将它绑定到一个bool?在我的ViewModel中。

在仔细研究之后,我发现IsChecked是一个普通的老布尔。found

我的第一直觉是添加一个附加的bool? " IsCheckedThreeState"属于MenuItem的属性,但我仍然无法弄清楚如何解决内部CheckBox绑定到不可空的IsChecked的事实。

如果没有更简单的方法,我可以创建一个新的控件模板并直接修改CheckBox,但我认为还需要对MenuItem进行子类化以将IsChecked调整为可为空。

那么,我是否必须继承/自定义MenuItem模板以获得我想要的功能,或者是否有一种我没想到的更简单的方法。感谢您提供的任何帮助。

3 个答案:

答案 0 :(得分:1)

好的,我已经做了一些挖掘。

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>

我有自己的资源词典。您必须在项目中添加一个引用,以及#34; PresentationFramework.Aero&#34;和以下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 - 一个 bool?,我绑定到内部CheckBox的IsChecked属性

2)ToggleIsSelected - 一个 bool ,我绑定到MenuItem的IsChecked属性

拥有这两个绑定允许用户单击MenuItem中的任意位置以切换CheckBox,如果IsSelected为null,则不会出现任何绑定错误。属性定义为:

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和相关的setter中删除ToggleIsSelected属性:

<Setter Property="IsChecked" Value="{Binding ToggleIsSelected}" />

...并将IsSelected属性更改为:

public bool? IsSelected
{
    get
    {
        return _isSelected;
    }
    set
    {
        if (value == _isSelected)
            return;
        _isSelected = value;
    OnPropertyChanged();
    }
}

这也将删除在任何地方点击切换的功能,因此您的用户必须直接检查CheckBox ...除非您想出其他方法来实现它。

我知道这是一个很长的答案......但我认为其他人可能需要这个。如果可以的话,还可以节省他们的时间。如果您能看到任何改进空间,请告诉我。

答案 1 :(得分:1)

最简单的选项可能是在<p>paragraph</p> <p>paragraph</p> <p>paragraph</p> <p>paragraph</p> <p>paragraph</p>属性中插入一个复选框。

Icon

<MenuItem Header="..." StaysOpenOnClick="True" Click="MenuItem_ToggleCheckBox"> <MenuItem.Icon> <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsThreeState="True" /> </MenuItem.Icon> </MenuItem> 事件处理程序中,您需要手动更新复选框(或复选框所绑定的某些属性)。

答案 2 :(得分:0)

如果您想保留原始的MenuItem检查样式,而只想显示空值,而不是让用户设置空值,则可以使用转换器来完成:

  1. 一个双向转换器ThreeStateToBooleanConverter,它可以将false / null / true更改为false / false / true(将false / true更改为false / true),将MenuItem绑定到bool时使用该转换器吗?财产。
  2. 一个单向转换器IsNullToVisibilityConverter,将null更改为Visible,将其他任何内容更改为Collapsed,可将其绑定为您希望在值为null时在MenuItem的Icon中显示的任何内容的Visibility。