如何在WPF中设置/重置三状态复选框值

如何在WPF中设置/重置三状态复选框值,wpf,wpf-controls,checkbox,wpfdatagrid,Wpf,Wpf Controls,Checkbox,Wpfdatagrid,我有一个datagrid,其标题列之一是三状态复选框。该列的celltemplate包含两个状态复选框 +AllItems复选框 -项目1 -项目2 -项目3 .. 我想使用AllItems复选框来选择/取消选择所有工作正常的项目(item1、item2)。接下来,我想将AllItems复选框设置为中间状态,即并非所有项目都已选中/未选中。同样,我想在手动选择所有项目时,将AllItems复选框设置为选中/未选中 这是我试过的代码 <dg:DataGridTemplateColumn.He

我有一个datagrid,其标题列之一是三状态复选框。该列的celltemplate包含两个状态复选框 +AllItems复选框 -项目1 -项目2 -项目3 .. 我想使用AllItems复选框来选择/取消选择所有工作正常的项目(item1、item2)。接下来,我想将AllItems复选框设置为中间状态,即并非所有项目都已选中/未选中。同样,我想在手动选择所有项目时,将AllItems复选框设置为选中/未选中

这是我试过的代码

<dg:DataGridTemplateColumn.HeaderTemplate>
    <DataTemplate>
        <StackPanel x:Name="StackPanelForItemCheckbox" Orientation="Horizontal">
           <CheckBox x:Name="AllItemSelectionCheckBox" HorizontalAlignment="Left" Cursor="Hand"  
                     IsChecked="{Binding IsAllItemChecked, Mode=TwoWay}"                                              
                     IsThreeState="True"  Checked="ItemSelectionCheckBox_Checked" 
                     Unchecked="ItemSelectionCheckBox_Unchecked"
                     Click="AllItemSelectionCheckBox_Click">
           <TextBlock x:Name="ItemNameTextBlock" Text="Item" Margin="10,0,0,0">
           ......
<dg:DataGridTemplateColumn.CellTemplate>
       <DataTemplate x:Name="ItemCheckDataTemplate">                                
           <StackPanel x:Name="ItemCheckBoxStackPanel" Orientation="Horizontal">                                    
                  <CheckBox x:Name="itemCheckBox" Cursor="Hand" IsChecked="{Binding IsItemChecked, Mode=TwoWay}" Click="ItemSelectionCheckBox_Click"></CheckBox>
                   <TextBlock x:Name="ItemNameTextBlock" Text="{Binding Path=Item}"> </TextBlock>                                   
            </StackPanel>
         </DataTemplate>
...
  • 我希望“AllItemSelectionCheckBox”复选框通过代码显示三种状态
  • 选中所有复选框将使其值为TRUE
  • 所有未选中的复选框将导致其值为FALSE
  • 选择的任何少数项都将导致其值为空
  • 代码示例如下:

    private void itemCheckBox_Checked(object sender, RoutedEventArgs e)
    { 
        DataGridRowsPresenter DGRPresenter = FindVisualChild<DataGridRowsPresenter>(DataGName1);
        if (DGRP == null || DGRP.Children == null)
            return null;
        foreach (object obj in UIEC)
        {
            DGR = obj as Microsoft.Windows.Controls.DataGridRow;
            UIC = DGR.Item as <datagrid mapped data model>;
            if (DGR.IsSelected == true)
                UIC.IsItemChecked = true;
            if (UIC.IsItemChecked == true)
                    NumberOfItemsChecked++;
        }
        if (NumberOfItemsChecked == myViewModelAllItems.Count)
        {
            allcheckbox.IsChecked = true;
        }
        else if (NumberOfItemsChecked < myViewModelAllItems.Count)
        {
            allcheckbox.IsChecked = null;   //intermittent state
        }
    }
    
    private void itemCheckBox\u选中(对象发送方,路由目标)
    { 
    DataGridRowsPresenter DGRPresenter=FindVisualChild(DataGName1);
    if(DGRP==null | | DGRP.Children==null)
    返回null;
    foreach(UIEC中的对象对象对象)
    {
    DGR=obj作为Microsoft.Windows.Controls.DataGridRow;
    UIC=DGR.项目组件;
    如果(DGR.IsSelected==true)
    UIC.IsItemChecked=true;
    如果(UIC.IsItemChecked==true)
    NumberOfItemsChecked++;
    }
    if(NumberOfItemsChecked==myViewModelAllItems.Count)
    {
    allcheckbox.IsChecked=true;
    }
    else if(NumberOfItemsChecked
    全局更新NumberOfItemsChecked计数不起作用,因为竞争条件会损坏外部的值。
    注意:上面的代码更像一个示例,可能无法直接复制它。我可以根据要求提供完整的代码和示例。

    事实上我有一个更好的

    我发现,如果我为IsThrestate创建了一个绑定,然后根据是否设置了该值来更改该值,那么它就可以工作了

    bool? _Value;
    public bool? Value
    {
        get { return _Value; }
        set
        {
            if (value == null)
            {
                IsThreeState = true;
            }
            else
            {
                IsThreeState = false;
            }
            _Value = value;
    
            NotifyPropertyChanged("Value");
        }
    }
    
    bool _IsThreeState = true;
    public bool IsThreeState
    {
        get { return _IsThreeState; }
        private set
        {
            _IsThreeState = value;
            NotifyPropertyChanged("IsThreeState");
        }
    }
    
    现在,如果外部将值设置为null,则复选框将支持三态。 如果该值为true或false,用户将无法将其设置为null。

    我找到了一种简单的方法:

    调整“三态事件点击”复选框,并检查其值是否为空。如果确实将其更改为false。像这样:

            private void cbAllFeatures_Click(object sender, RoutedEventArgs e)
            {
                if (cbAllFeatures.IsChecked != null) return;
    
                cbAllFeatures.IsChecked = false;
            }
    
    

    我希望你喜欢。

    这就是我的工作。我创建了一个新的FTThreeStateCheckBox(从CheckBox子类化),覆盖了IsCheckedProperty元数据,以便我可以监视值中的更改,然后如果它为null,我将IsThrestate设置为false,并将IsChecked设置为false

    static FTThreeStateCheckBox()
    {
        IsCheckedProperty.OverrideMetadata(typeof(FTThreeStateCheckBox), new FrameworkPropertyMetadata(null, IsCheckedChanged));          
    }
    
    
    public static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((d as FTThreeStateCheckBox).IsChecked == null)
        {
            (d as FTThreeStateCheckBox).IsThreeState = false;
            (d as FTThreeStateCheckBox).IsChecked = false;
        }            
    }
    

    对于想要实现这一目标的人

    • 用户只能将视图中的复选框切换为true或false。不是不确定
    • ViewModel中的代码可以将复选框切换为true、false或undefined
    只要这样做:

    • 添加复选框以查看,但设置
      IsThrestate=true
    • IsChecked
      绑定到ViewModel中的
      bool?
      (可为空的bool)属性

    您现在可以在ViewModel中将其设置为
    null
    ,而用户无法在视图中执行此操作。

    次要简化:
    IsThrestate=!value.HasValue
    这应该是答案!我很好奇为什么这不是最初的答案之一。您知道框架行为是否发生了变化吗?这似乎是一件非常明显的事情,它实际上是自动发生的。也许我不明白OP的问题,但当我来搜索时,这回答了我的问题。@andrew我认为它没有改变。微软已经十年没有更新wpf了。这个解决方案有效,但我只是偶然发现的。关闭三态是不直观的,但它仍然具有三态行为。在我发现这一点之前,我在这里投票并使用了其他的解决方案,但也遇到了一些问题。我的措辞很糟糕,听起来很傲慢(“真的很明显…”)。这对我来说当然不明显。我只是想说我很惊讶一个简单的解决方案不是一个早期已知的解决方案。在我真正尝试(预先计划)之前,我一直在研究这个问题,当我读到你的答案时,我觉得“很好,这很简单”。然后我想知道当另一个答案写出来时,这种行为是否有所不同。有时候,我喜欢在做事情之前先阅读这篇文章,以了解自己正在做什么——特别是WPF:)谢谢!
    static FTThreeStateCheckBox()
    {
        IsCheckedProperty.OverrideMetadata(typeof(FTThreeStateCheckBox), new FrameworkPropertyMetadata(null, IsCheckedChanged));          
    }
    
    
    public static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if ((d as FTThreeStateCheckBox).IsChecked == null)
        {
            (d as FTThreeStateCheckBox).IsThreeState = false;
            (d as FTThreeStateCheckBox).IsChecked = false;
        }            
    }