Home > Mobile >  Create a CheckBox that will depend on other CheckBoxes that are inside the ItemsControl
Create a CheckBox that will depend on other CheckBoxes that are inside the ItemsControl

Time:01-03

How can I make the CheckBox above (All) depend on the other three in a good way.

That is, if they are all Checked, then it will be Checked, if all are UnChecked, then it will be UnChecked,

And if some of them are Checked, then it will be Null

public ObservableCollection<string> Items { get; set; } = new() { "A", "B", "C" };
<StackPanel>
    <CheckBox Content="All"/>
    <ItemsControl ItemsSource="{Binding Items}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <CheckBox/>
                    <TextBlock Text="{Binding}"/>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>

CodePudding user response:

Iterating through UI is a bad idea, it is complicated. You should use mvvm and work with data rather than relying on ui as a data store.

How would that work?

You should bind a collection of item viewmodels. A class exposing both a bool ischecked property and a string description.

I am using the CommunityToolkit.Mvvm package here. That generates code including public properties using attributes.

Here's my viewmodel for each item

using CommunityToolkit.Mvvm.ComponentModel;

namespace WpfApp3
{
    public partial class ItemViewModel : ObservableObject
    {
        [ObservableProperty]
        private bool? isChecked;

        [ObservableProperty]
        private string description;

        public ItemViewModel(bool? _isChecked, string _description)
        {
            isChecked = _isChecked;
            description = _description;
        }
    }
}

I want to bind both those properties and I'll need a window viewmodel exposes a public property for those items.

I'll create a mainwindowviewmodel, but here's the view:

            Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
        <Grid>
        <StackPanel>
        <CheckBox Content="All"
                  IsChecked="{Binding AllChecked, Mode=TwoWay}"    
                      />
        <ItemsControl ItemsSource="{Binding Items}"
                          >
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"
                                  Content="{Binding Description}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</Window>

All is bound to a property in my mainwindowviewmodel.

Here's my mainwindowviewmodel:

    using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;

namespace WpfApp3
{
    public partial class MainWindowViewModel : ObservableObject
    {
        [ObservableProperty]
        private bool? allChecked = false;


        [ObservableProperty]
        private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>
        { 
            new ItemViewModel((bool?)null, "A"),
            new ItemViewModel(true, "B"),
            new ItemViewModel(false, "C"),
        };

        public MainWindowViewModel() 
        {
            foreach (var item in items)
            {
                item.PropertyChanged  = Item_PropertyChanged;
            }    
        }

        private void Item_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsChecked") 
            {
                reDoAll(sender);
            }
        }

        private void reDoAll(object? s)
        {
            var item = s as ItemViewModel;
            Debug.WriteLine(item.Description);
            if (items.All(x => x.IsChecked == true))
            {
                AllChecked = true;
                return;
            }
            if (items.All(x => x.IsChecked == false))
            {
                AllChecked = false;
                return;
            }
            AllChecked = (bool?)null;
        }
    }
}

When you change any ischecked, the property changed handler runs redDoAll which applies your logic and sets AllChecked. The All checkbox in the view responds.

  •  Tags:  
  • wpf
  • Related