TreeView Select parent checkbox based on children – WPF MVVM

38 viewscheckboxmvvmtreeviewwpf
0

I am new to WPF. I found a few examples and put together a tree-view example in C# WPF Mvvm. I got the children’s checkboxes checked or unchecked based on parent selection. I am not sure how to access the parent binding or property to check or uncheck it based on children. If all children are selected, I expect parents to be checked, and if even one child is unchecked, I want parents to be unchecked. How do I achieve this using the MVVM viewmodel? Any suggestions would be greatly appreciated.

Xaml View

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp1"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
    <local:MainViewModel></local:MainViewModel>
</Window.DataContext>
<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:CheckableItem}" ItemsSource="{Binding 
        Children, Mode=TwoWay}">
        <StackPanel Orientation="Horizontal">
            <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeView}}, 
                                                Path=DataContext.CheckBoxCommand}" CommandParameter="{Binding}"/>
            <TextBlock Text="{Binding Name}"/>
        </StackPanel>
    </HierarchicalDataTemplate>
</Window.Resources>

<StackPanel>
    <Label Content="Miscellaneous Imports" HorizontalAlignment="Center" />
    <ScrollViewer>
        <TreeView ItemsSource="{Binding MiscellaneousImports, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="10" Height="450"/>
    </ScrollViewer>
</StackPanel>

ViewModel

public class MainViewModel : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler? PropertyChanged;

    public ICommand CheckBoxCommand { get; set; }
    public MainViewModel()
    {
        LoadCheckableItems();
        CheckBoxCommand = new DelegateCommand<CheckableItem>(OnCheckBoxChecked);
    }

    private void OnCheckBoxChecked(CheckableItem item)
    {
        throw new NotImplementedException();
    }

    private ObservableCollection<CheckableItem> miscellaneousImports;
    public ObservableCollection<CheckableItem> MiscellaneousImports
    {
        
        get { return miscellaneousImports; }
        set
        {
            miscellaneousImports = value;
            OnPropertyChanged("MiscellaneousImports");
        }
    }
    private void LoadCheckableItems()
    {
        List<CheckableItem> lstItems = new List<CheckableItem>();

        CheckableItem item = new CheckableItem
        { Name = "Coffee", Children = new ObservableCollection<CheckableItem>() { new CheckableItem { Name="Medium" }, new CheckableItem {Name="Dark" } } };
        lstItems.Add(item);

        MiscellaneousImports = new ObservableCollection<CheckableItem> ( lstItems );
    }
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Checkbox collection class

public class CheckableItem:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
        public ObservableCollection<CheckableItem> Children { get; set; }
        private bool _isChecked;
        public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                _isChecked = value;
                OnPropertyChanged("IsChecked");
                if (Children != null)
                {
                    foreach (CheckableItem child in Children)
                    {
                        child.IsChecked = IsChecked;
                    }
                    OnPropertyChanged(nameof(Children));
                }
            }
        }
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
     
    }