Checkbox in WinUI TreeView

8 Jul 202624 minutes to read

The TreeView provides support for loading a CheckBox in each node and allows users to check or uncheck the corresponding node. To do this, add a checkbox in the ItemTemplate of the TreeView and bind the IsChecked property of the TreeViewNode.

Working with CheckBox in Bound Mode

When you are populating TreeView nodes from the ItemsSource, you can get or set the checked items by using the CheckedItems property.

The TreeView supports checking multiple items by binding the CheckedItems property from the view model with an ObservableCollection<object> type.

NOTE

Set ItemTemplateDataContextType as Node to bind the TreeViewNode.IsChecked property to the CheckBox in the ItemTemplate.

NOTE

The TreeView processes and sets TreeViewNode.IsChecked based on CheckedItems only when ItemsSource is bound.

<Page x:Class="syncfusion.treeviewdemos.winui.CheckBoxPage"
      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="using:syncfusion.treeviewdemos.winui"
      xmlns:treeView="using:Syncfusion.UI.Xaml.TreeView"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
      NavigationCacheMode="Disabled">

<Page.DataContext>
    <local:CheckBoxView />
</Page.DataContext>

<Grid>
    <treeView:SfTreeView x:Name="treeView"
                           AutoExpandMode="AllNodes"
                           BorderBrush="LightGray"
                           BorderThickness="1"
                           CheckBoxMode="Recursive"
                           CheckedItems="{Binding CheckedItems}"
                           ChildPropertyName="Models"
                           ExpandActionTrigger="Expander"
                           IsAnimationEnabled="True"
                           ItemTemplateDataContextType="Node"
                           ItemsSource="{Binding Items}"
                           NodePopulationMode="Instant">
                    <treeView:SfTreeView.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <CheckBox x:Name="checkBox"
                                          Content="{Binding Content.State}"
                                          local:NullableTreeCheckbox.IsEnabled="true"
                                          local:NullableTreeCheckbox.IsChecked="{Binding IsChecked, Mode=TwoWay}" />
                            </Grid>
                        </DataTemplate>
                    </treeView:SfTreeView.ItemTemplate>
    </treeView:SfTreeView>
</Grid>
</Page>
public class CheckBoxView : NotificationObject
{ 
    public ObservableCollection<object> CheckedItems { get; set; }

    public ObservableCollection<CheckBoxModel> Items { get; set; }

    public CheckBoxView()
    {
        Items = new ObservableCollection<CheckBoxModel>();

        var country1 = new CheckBoxModel { State = "Australia" };
        var country2 = new CheckBoxModel { State = "Brazil" };
        var country3 = new CheckBoxModel { State = "China" };
        var country4 = new CheckBoxModel { State = "France" };
        var country5 = new CheckBoxModel { State = "India" };
        var country6 = new CheckBoxModel { State = "USA" };
        var country7 = new CheckBoxModel { State = "UK" };
        var country8 = new CheckBoxModel { State = "Russia" };
        var country9 = new CheckBoxModel { State = "Canada" };
        var country10 = new CheckBoxModel { State = "Germany" };

        var aus_state1 = new CheckBoxModel { State = "New South Wales" };
        var aus_state2 = new CheckBoxModel { State = "Victoria" };
        var aus_state3 = new CheckBoxModel { State = "South Australia" };
        var aus_state4 = new CheckBoxModel { State = "Western Australia" };

        var brazil_state1 = new CheckBoxModel { State = "Parana" };
        var brazil_state2 = new CheckBoxModel { State = "Ceara" };
        var brazil_state3 = new CheckBoxModel { State = "Acre" };

        var china_state1 = new CheckBoxModel { State = "Guangzhou" };
        var china_state2 = new CheckBoxModel { State = "Shanghai" };
        var china_state3 = new CheckBoxModel { State = "Beijing" };
        var china_state4 = new CheckBoxModel { State = "Shantou" };

        var france_state1 = new CheckBoxModel { State = "Pays de la Loire" };
        var france_state2 = new CheckBoxModel { State = "Aquitaine" };
        var france_state3 = new CheckBoxModel { State = "Brittany" };
        var france_state4 = new CheckBoxModel { State = "Lorraine" };

        var ind_State1 = new CheckBoxModel { State = "Assam" };
        var ind_State2 = new CheckBoxModel { State = "Bihar" };
        var ind_State3 = new CheckBoxModel { State = "Tamil Nadu" };
        var ind_State4 = new CheckBoxModel { State = "Punjab" };

        var usa_state1 = new CheckBoxModel { State = "New York" };
        var usa_state2 = new CheckBoxModel { State = "California" };
        var usa_state3 = new CheckBoxModel { State = "Texas" };
        var usa_state4 = new CheckBoxModel { State = "Washington" };
        var usa_state5 = new CheckBoxModel { State = "Florida" };

        var uk_state1 = new CheckBoxModel { State = "England" };
        var uk_state2 = new CheckBoxModel { State = "Wales" };
        var uk_state3 = new CheckBoxModel { State = "Scotland" };
        var uk_state4 = new CheckBoxModel { State = "Northern Ireland" };

        var russia_state1 = new CheckBoxModel { State = "Mordovia" };
        var russia_state2 = new CheckBoxModel { State = "Chechnya" };
        var russia_state3 = new CheckBoxModel { State = "Tatarstan" };
        var russia_state4 = new CheckBoxModel { State = "Dagestan" };

        var canada_state1 = new CheckBoxModel { State = "Alberta" };
        var canada_state2 = new CheckBoxModel { State = "Manitoba" };
        var canada_state3 = new CheckBoxModel { State = "New Brunswick" };
        var canada_state4 = new CheckBoxModel { State = "Quebec" };

        var germany_state1 = new CheckBoxModel { State = "Berlin" };
        var germany_state2 = new CheckBoxModel { State = "Hamburg" };
        var germany_state3 = new CheckBoxModel { State = "Bremen" };
        var germany_state4 = new CheckBoxModel { State = "Lower Saxony" };

        country1.Models.Add(aus_state1);
        country1.Models.Add(aus_state2);
        country1.Models.Add(aus_state3);
        country1.Models.Add(aus_state4);

        country2.Models.Add(brazil_state1);
        country2.Models.Add(brazil_state2);
        country2.Models.Add(brazil_state3);

        country3.Models.Add(china_state1);
        country3.Models.Add(china_state2);
        country3.Models.Add(china_state3);
        country3.Models.Add(china_state4);

        country4.Models.Add(france_state1);
        country4.Models.Add(france_state2);
        country4.Models.Add(france_state3);
        country4.Models.Add(france_state4);

        country5.Models.Add(ind_State1);
        country5.Models.Add(ind_State2);
        country5.Models.Add(ind_State3);
        country5.Models.Add(ind_State4);

        country6.Models.Add(usa_state1);
        country6.Models.Add(usa_state2);
        country6.Models.Add(usa_state3);
        country6.Models.Add(usa_state4);
        country6.Models.Add(usa_state5);

        country7.Models.Add(uk_state1);
        country7.Models.Add(uk_state2);
        country7.Models.Add(uk_state3);
        country7.Models.Add(uk_state4);

        country8.Models.Add(russia_state1);
        country8.Models.Add(russia_state2);
        country8.Models.Add(russia_state3);
        country8.Models.Add(russia_state4);

        country9.Models.Add(canada_state1);
        country9.Models.Add(canada_state2);
        country9.Models.Add(canada_state3);
        country9.Models.Add(canada_state4);

        country10.Models.Add(germany_state1);
        country10.Models.Add(germany_state2);
        country10.Models.Add(germany_state3);
        country10.Models.Add(germany_state4);

        Items.Add(country1);
        Items.Add(country2);
        Items.Add(country3);
        Items.Add(country4);
        Items.Add(country5);
        Items.Add(country6);
        Items.Add(country7);
        Items.Add(country8);
        Items.Add(country9);
        Items.Add(country10);

        CheckedItems = new ObservableCollection<object>();
        CheckedItems.Add(aus_state3);
        CheckedItems.Add(aus_state4);
        CheckedItems.Add(brazil_state2);
        CheckedItems.Add(brazil_state3);
        CheckedItems.Add(china_state3);
    }
}

public class NullableTreeCheckbox : DependencyObject
{
    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(NullableTreeCheckbox), new PropertyMetadata(false, OnIsEnabledChanged));

    public static readonly DependencyProperty IsCheckedProperty =
        DependencyProperty.RegisterAttached("IsChecked", typeof(object),
        typeof(NullableTreeCheckbox), new PropertyMetadata(default(object), IsCheckedChanged));

    private static readonly DependencyProperty IsInternalCheckedProperty =
        DependencyProperty.RegisterAttached("IsInternalChecked", typeof(object),
        typeof(NullableTreeCheckbox), new PropertyMetadata(null, OnIsInternalCheckedChanged));

    public static bool GetIsEnabled(DependencyObject obj)
    {
        if (obj == null)
            return false;

        return (bool)obj.GetValue(IsEnabledProperty);
    }

    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        if (obj == null)
            return;
        obj.SetValue(IsEnabledProperty, value); 
    }

    public static object GetIsChecked(DependencyObject obj)
    {
        if (obj == null)
            return false;

        return (bool?)obj.GetValue(IsCheckedProperty);
    }

    public static void SetIsChecked(DependencyObject obj, object value)
    {
        if (obj == null)
            return;

        obj.SetValue(IsCheckedProperty, value);
    }

    private static void OnIsInternalCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SetIsChecked(d, (bool?)e.NewValue);
    }

    private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var checkbox = d as Microsoft.UI.Xaml.Controls.CheckBox;
        if ((bool)e.NewValue)
        {
            var binding = new Binding
            {
                Path = new PropertyPath("IsChecked"),
                Mode = BindingMode.TwoWay,
                Source = checkbox,
            };
            checkbox.SetBinding(NullableTreeCheckbox.IsInternalCheckedProperty, binding);
        }
    }

    private static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var checkbox = d as Microsoft.UI.Xaml.Controls.CheckBox;
        bool? newValue = null;
        if (e.NewValue is bool?)
            newValue = (bool?)e.NewValue;
        else if (e.NewValue != null)
            newValue = (bool)e.NewValue;
        if (!checkbox.IsChecked.Equals(newValue))
            checkbox.IsChecked = newValue;
    }
}
public class CheckBoxModel : NotificationObject
{
    private ObservableCollection<CheckBoxModel> models;

    private string state;

    public CheckBoxModel()
    {
        Models = new ObservableCollection<CheckBoxModel>();
    }

    public ObservableCollection<CheckBoxModel> Models
    {
        get
        {
            return models;
        }

        set
        {
            models = value;
            this.RaisePropertyChanged(nameof(Models));
        }
    }
      
    public string State
    {
        get
        {
            return state;
        }

        set
        {
            state = value;
            this.RaisePropertyChanged(nameof(State));
        }
    }      
}

WinUI TreeView with CheckBoxes

NOTE

View sample in GitHub

Working with CheckBox in Unbound Mode

You can directly set the checkbox state by setting the TreeViewNode.IsChecked property value when creating nodes.

<Page x:Class="syncfusion.treeviewdemos.winui.CheckBoxPage"
      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="using:syncfusion.treeviewdemos.winui"
      xmlns:treeView="using:Syncfusion.UI.Xaml.TreeView"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
      NavigationCacheMode="Disabled">

<Grid>
    <treeView:SfTreeView x:Name="TreeView"
                           Width="400"
                           BorderThickness="1"
                           BorderBrush="LightGray"
                           IsAnimationEnabled="True"
                           ItemTemplateDataContextType="Node"
                           CheckBoxMode="Recursive">
                    <treeView:SfTreeView.Nodes>
                        <treeView:TreeViewNode Content="Grains"
                                             IsExpanded="True"
                                             IsChecked="True">
                            <treeView:TreeViewNode.ChildNodes>
                                <treeView:TreeViewNode Content="Cereals"
                                                     IsExpanded="True">
                                    <treeView:TreeViewNode.ChildNodes>
                                        <treeView:TreeViewNode Content="Rice"
                                                             IsChecked="True" />
                                        <treeView:TreeViewNode Content="Barley" />
                                    </treeView:TreeViewNode.ChildNodes>
                                </treeView:TreeViewNode>
                                <treeView:TreeViewNode Content="Oilseeds">
                                    <treeView:TreeViewNode.ChildNodes>
                                        <treeView:TreeViewNode Content="Safflower"
                                                             IsChecked="True" />
                                    </treeView:TreeViewNode.ChildNodes>
                                </treeView:TreeViewNode>
                            </treeView:TreeViewNode.ChildNodes>
                        </treeView:TreeViewNode>
                        <treeView:TreeViewNode Content="Fruits"
                                             IsExpanded="true">
                            <treeView:TreeViewNode.ChildNodes>
                                <treeView:TreeViewNode Content="Orange"
                                                     IsChecked="True" />
                                <treeView:TreeViewNode Content="Apples"
                                                     IsExpanded="true" />
                            </treeView:TreeViewNode.ChildNodes>
                        </treeView:TreeViewNode>
                        <treeView:TreeViewNode Content="Vegetables"
                                             IsExpanded="true"
                                             IsChecked="True">
                            <treeView:TreeViewNode.ChildNodes>
                                <treeView:TreeViewNode Content="Root Vegetables"
                                                     IsExpanded="true">
                                    <treeView:TreeViewNode.ChildNodes>
                                        <treeView:TreeViewNode Content="Potato"
                                                             IsChecked="True" />
                                        <treeView:TreeViewNode Content="Carrot" />
                                    </treeView:TreeViewNode.ChildNodes>
                                </treeView:TreeViewNode>
                                <treeView:TreeViewNode Content="Podded">
                                    <treeView:TreeViewNode.ChildNodes>
                                        <treeView:TreeViewNode Content="Peanut"
                                                             IsChecked="True" />
                                        <treeView:TreeViewNode Content="Lentil" />
                                    </treeView:TreeViewNode.ChildNodes>
                                </treeView:TreeViewNode>
                            </treeView:TreeViewNode.ChildNodes>
                        </treeView:TreeViewNode>
                    </treeView:SfTreeView.Nodes>

                    <treeView:SfTreeView.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <CheckBox x:Name="checkBox"
                                          Content="{Binding Content}"
                                          local:NullableTreeCheckbox.IsEnabled="true"
                                          local:NullableTreeCheckbox.IsChecked="{Binding IsChecked, Mode=TwoWay}" />
                            </Grid>
                        </DataTemplate>
                    </treeView:SfTreeView.ItemTemplate>
    </treeView:SfTreeView>
</Grid>
</Page>
public class NullableTreeCheckbox : DependencyObject
{
    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(NullableTreeCheckbox), new PropertyMetadata(false, OnIsEnabledChanged));

    public static readonly DependencyProperty IsCheckedProperty =
        DependencyProperty.RegisterAttached("IsChecked", typeof(object),
        typeof(NullableTreeCheckbox), new PropertyMetadata(default(object), IsCheckedChanged));

    private static readonly DependencyProperty IsInternalCheckedProperty =
        DependencyProperty.RegisterAttached("IsInternalChecked", typeof(object),
        typeof(NullableTreeCheckbox), new PropertyMetadata(null, OnIsInternalCheckedChanged));

    public static bool GetIsEnabled(DependencyObject obj)
    {
        if (obj == null)
            return false;

        return (bool)obj.GetValue(IsEnabledProperty);
    }

    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        if (obj == null)
            return;
        obj.SetValue(IsEnabledProperty, value); 
    }

    public static object GetIsChecked(DependencyObject obj)
    {
        if (obj == null)
            return false;

        return (bool?)obj.GetValue(IsCheckedProperty);
    }

    public static void SetIsChecked(DependencyObject obj, object value)
    {
        if (obj == null)
            return;

        obj.SetValue(IsCheckedProperty, value);
    }

    private static void OnIsInternalCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SetIsChecked(d, (bool?)e.NewValue);
    }

    private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var checkbox = d as Microsoft.UI.Xaml.Controls.CheckBox;
        if ((bool)e.NewValue)
        {
            var binding = new Binding
            {
                Path = new PropertyPath("IsChecked"),
                Mode = BindingMode.TwoWay,
                Source = checkbox,
            };
            checkbox.SetBinding(NullableTreeCheckbox.IsInternalCheckedProperty, binding);
        }
    }

    private static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var checkbox = d as Microsoft.UI.Xaml.Controls.CheckBox;
        bool? newValue = null;
        if (e.NewValue is bool?)
            newValue = (bool?)e.NewValue;
        else if (e.NewValue != null)
            newValue = (bool)e.NewValue;
        if (!checkbox.IsChecked.Equals(newValue))
            checkbox.IsChecked = newValue;
    }
}

WinUI TreeView Nodes with Checkboxes

NOTE

View sample in GitHub

CheckBox State

The TreeView processes the IsChecked property (checkbox state) of a TreeViewNode based on the CheckBoxMode property. The CheckBoxMode defines how the parent and child node’s checkbox state is updated when the user checks or unchecks a node. By default, its value is None.

The CheckBox mode contains the following three states:

  • None: Check and uncheck will be updated only in the view, but they will not affect the CheckedItems collection.
  • Individual: This Checkbox state affects the individual node only, and it does not affect the parent node or child node checkbox state or IsChecked property value.
  • Recursive: Check and uncheck of a node affects the parent and child node checkbox state. For example, if a parent node’s checkbox state is checked or unchecked, all of its child node checkbox states will be updated. If all child nodes are checked or unchecked within the parent node, the parent node checkbox state will be updated. If any of the child nodes are checked and some are not, the parent node will be in the intermediate state.
<treeView:SfTreeView x:Name="treeView" CheckBoxMode="Recursive" />
treeView.CheckBoxMode = CheckBoxMode.Recursive;

NOTE

In recursive mode, the parent node’s checkbox state or IsChecked property value is updated only through UI interaction.

Get or Set Checked Items

Get or Set Checked Items in Bound Mode

You can get or set a list of items to be checked or unchecked by using the CheckedItems property.

When the CheckBoxMode is other than None, an individual TreeViewNode or a collection of TreeViewNode objects can be checked from the code by setting the CheckedItems property, or by adding items to the CheckedItems property based on the CheckBoxMode.

NOTE

Programmatically adding or removing a node value does not affect the parent and child node checkbox states.

treeView.CheckedItems.Add(viewModel.Items[2]);
treeView.CheckedItems.Add(viewModel.Items[3]);

Events

NodeChecked event

The NodeChecked event is raised when checking or unchecking a checkbox at run time. The NodeCheckedEventArgs has the following members, which provide information for the NodeChecked event.

  • Node: Gets the TreeViewNode and the data associated with the checked item as its arguments.
treeView.NodeChecked += treeView_NodeChecked;

private void treeView_NodeChecked(object sender, NodeCheckedEventArgs e)
{
    
}

NOTE

NodeChecked event occurs only in UI interactions.