开发者

How to set/reset Three-state checkbox value in WPF

开发者 https://www.devze.com 2023-02-06 02:50 出处:网络
I have a datagrid whose one of the header column is Three-state checkbox. The celltemplate for that column contains two state checkbox

I have a datagrid whose one of the header column is Three-state checkbox. The celltemplate for that column contains two state checkbox + AllItems CheckBox - Item1 - Item2 - Item3 .. I wanted to use AllItems checkbox to select/unselect all items (item1,item2) which works fine. Next I wanted to set AllItems checkbox to intermediate state when not all items are selected/unselected. Similarly I wanted to set AllItems checkbox as checked/unchecked when all items get manually selected.

Here is the code that I tried...

<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" method looks for all three state (all-checked, none-checked, intermediate) and sets "IsAllItemChecked" property which is INotifyproperty. It does not work. Other alternative I may try is to find the "AllItems" element and set it from the code. Could not locate anything like that on web. There is few examples but i开发者_如何学JAVAs for TreeView and not the way I am trying. Any help?

PS>>

Updated with fix to close this post.

  • First thing I wanted was to allow "AllItemSelectionCheckBox" to have only two states (True, False) when manually selected.

    private void AllItemSelectionCheckBox_Click(object sender, RoutedEventArgs e)
    {
        var cb = e.Source as CheckBox;
        if (!cb.IsChecked.HasValue)
            cb.IsChecked = false;  
    }
    
  • I wanted "AllItemSelectionCheckBox" checkbox to show three-state thru code.
  • All check-box checked will cause its value to TRUE
  • All check-box unchecked will cause its value to FALSE
  • Any few selected will cause its value to NULL.

Code Example following:

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

Updating NumberOfItemsChecked count globally did not work due to race condition corrupting the value outside.

Note: Above code is more like an example and may not work copying it directly. I can provide complete code with sample on request.


Actually I got one better.

I found out that if I create a binding for IsThreeState, then change the value based on whether the value is set or not, then it works.

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");
    }
}

Now the checkbox will support threestate IFF the value is set to null externally. If the value is true or false, the user won't be able to set it to null.


for people who want to achieve this

  • user can switch checkbox in the view only to true or false. Not to undefined!
  • code in ViewModel can switch checkbox to true or false or undefined

just do this:

  • add checkbox to view but DONT set IsThreeState=true
  • bind IsChecked to a bool? (nullable bool) property in the ViewModel

You can now set it to null in the ViewModel while the user cannot do it in the View.


I found a simple way:

Adjust the event click of three state check box, and check if its value is null. If does change it to false. Like this:

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

            cbAllFeatures.IsChecked = false;
        }

I hope you enjoy it.


This is what worked for me. I created a new FTThreeStateCheckBox (subclassed from CheckBox), overrode the IsCheckedProperty metadata so I could watch for changes in the value, then if it was null, I set IsThreeState and IsChecked to 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;
    }            
}
0

精彩评论

暂无评论...
验证码 换一张
取 消