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

10

我有一个数据网格,其中一个表头列是三态复选框。该列的单元格模板包含两个状态的复选框: + 全部项目的复选框 - 项目1 - 项目2 - 项目3 .. 我想使用“全部项目”复选框来选择/取消选择所有项目(项目1、项目2),这个功能可以正常工作。接下来,我想在没有全部选择/取消选择所有项目时将“全部项目”复选框设置为中间状态。同样地,当手动选择所有项目时,我想将“全部项目”复选框设置为已选/未选。

以下是我尝试过的代码...

<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>
...
"ItemSelectionCheckBox_Click"方法检查所有三种状态(全部选中,全部未选中,部分选中)并设置“IsAllItemChecked”属性,该属性是INotifyProperty。但它没有起作用。我可以尝试的另一种选择是在代码中查找"AllItems"元素并进行设置。在网上没有找到类似的内容。有一些示例,但都是针对TreeView而非我所尝试的方式。有帮助吗? PS>> 已更新,加入修复以关闭此帖子。
  • 首先,我想允许手动选择“AllItemSelectionCheckBox”时只有两种状态(True、False)。

private void AllItemSelectionCheckBox_Click(object sender, RoutedEventArgs e)
{
    var cb = e.Source as CheckBox;
    if (!cb.IsChecked.HasValue)
        cb.IsChecked = false;  
}
  • 我希望通过代码让" AllItemSelectionCheckBox "复选框呈现三种状态。
  • 所有复选框都选中会将其值设置为TRUE
  • 所有复选框都未选中会将其值设置为FALSE
  • 选择了任意几个复选框会将其值设置为NULL。
  • 以下是示例代码:

    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
        }
    }
    

    全局更新NumberOfItemsChecked计数器由于竞态条件损坏了外部值。

    注意:以上代码更像是一个示例,直接复制可能无法工作。如果需要,我可以提供完整的代码和样本。

    4个回答

    19

    实际上,我找到了一个更好的方法。

    我发现如果我为IsThreeState创建一个绑定,然后根据值是否设置来改变该值,那么它就能够工作。

    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。


    3
    简化版翻译:IsThreeState = !value.HasValue 可以翻译为“如果value没有值,则IsThreeState为真”。 - Dave Mackersie
    这应该是答案! - sebingel

    5

    适用于希望实现此功能的人

    • 用户只能在视图中将复选框切换为true或false。不能为undefined!
    • ViewModel中的代码可以将复选框切换为true、false或undefined

    只需执行以下操作:

    • 将复选框添加到视图中,但不要设置IsThreeState=true
    • IsChecked绑定到ViewModel中的bool?(可空布尔)属性

    您现在可以在ViewModel中将其设置为null,而用户无法在View中这样做。


    我很好奇为什么这不是原始答案之一。您知道框架行为是否发生了变化吗?这似乎是非常明显要尝试的事情,并且它基本上会自动发生。也许我不理解OP的问题,但当我在搜索时,这个回答了我的问题。 - andrew
    1
    @andrew 我认为它没有改变。微软已经十年没有更新WPF了。这个解决方案有效,但我是偶然发现的。关闭三态并不直观,但它仍然具有三态行为。在找到这个解决方案之前,我也赞同并使用了其他解决方案,但遇到了一些问题。 - Welcor
    我的措辞很差,听起来很傲慢(“...非常明显…”)。对我来说确实不明显。我只是想说,我很惊讶一个简单的解决方案不是早期已知的解决方案。在实际尝试之前(预先规划),我正在研究这个问题,当我看到你的答案时,我想:“好极了,很简单。”然后我想知道在另一个答案编写时行为是否不同。有时我喜欢在做事情之前阅读 SO ,以便了解自己正在从事什么 - 特别是 WPF :) 谢谢! - andrew
    @Blechdose 谢谢,这对我帮助很大! - Zoli

    1
    我发现了一个简单的方法:
    调整三态复选框的点击事件,并检查其值是否为空。如果是,将其更改为false。就像这样:
            private void cbAllFeatures_Click(object sender, RoutedEventArgs e)
            {
                if (cbAllFeatures.IsChecked != null) return;
    
                cbAllFeatures.IsChecked = false;
            }
    
    

    我希望你喜欢它。


    0

    这是对我有效的方法。我创建了一个新的FTThreeStateCheckBox(从CheckBox派生),覆盖了IsCheckedProperty元数据,以便我可以监视值的变化,然后如果它为null,我将IsThreeState和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;
        }            
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接