How to Create TreeView in XAML with CheckBoxes.

madaxe2020

Well-known member
Joined
Sep 7, 2020
Messages
50
Programming Experience
5-10
Morning,

I have a 'View Model' that creates a data structure that is variable and up to 6 levels deep. I'm struggling writing the XAML to display the treeview can somebody help, the treeview should also have check boxes also.

the goal will be to set the parents check box if all children are checked and so on up the treeview.

thanks

madaxe


ViewModel:
using System.Collections.Generic;
using treeview.Models;

namespace treeview.ViewModels
{
    public class ThingViewModel: ViewModelBase
    {
        ThingModel _thingModel;
        public bool? IsChecked { get; set; }
        public bool IsInitiallySelected { get; set; }
        public ThingModel Parent => _thingModel.Parent;
        public List<ThingModel> Children => _thingModel.Children;
        public string Name => _thingModel.Name;
        public List<ThingViewModel> ViewModelChildren => GetViewModelChildren();
        public ThingViewModel ViewModelParent => GetViewModelParent();

        public ThingViewModel(ThingModel thingModel)
        {
            _thingModel = thingModel;
        }

        private List<ThingViewModel> GetViewModelChildren()
        {
            List<ThingViewModel> thingViewModels = new List<ThingViewModel>();

            foreach(ThingModel thingModel in Children)
            {
                thingViewModels.Add(new ThingViewModel(thingModel));
            }
            return thingViewModels;
        }

        private ThingViewModel GetViewModelParent()
        {
            return new ThingViewModel(Parent);
        }
    }
}
 
Solution
final working view model

ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using treeview.Commands;
using treeview.Models;

namespace treeview.ViewModels
{
    public class ThingViewModel : ViewModelBase, IThingViewModel
    {
        ThingModel _thingModel;
        
        public Guid guid;

        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get
            {
                return _isChecked;
            }
            set
            {
                _isChecked = value;
                OnPropertyChanged(nameof(IsChecked));
                OnPropertyChanged(nameof(State));
            }
        }

        public bool? AllChildrenChecked...
Did you look and try the following?

As an aside, lines 14-15 of your view model look very wrong. Each time those properties are called, brand new objects will be returned which is going to utterly confuse the WPF view bindings.
 
Here is my treeview, but it wont bind to the current data structure and i dont kow why


xaml:
<Window x:Class="treeview.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:treeview"
        xmlns:interface="clr-namespace:treeview.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid>
        <Label Content="{Binding TestLabel}"/>

        <TreeView ItemsSource="{Binding ThingViewModels}" Margin="10" Height="300">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding ViewModelChildren}" DataType="{x:Type interface:IThingViewModel}">
                    <TreeViewItem Header="{Binding Name}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

xaml.cs:
using System.Windows;
using treeview.ViewModels;

namespace treeview
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ThingsDataContext(new ThingsViewModel());
        }
    }
}

DataContext:
using treeview.Models;
using treeview.ViewModels;

namespace treeview
{
    public class ThingsDataContext:ViewModelBase
    {
        private ThingsViewModel _thingsViewModel;

        private ThingViewModel _thingViewModel;
        public ThingViewModel ThingViewModels => _thingViewModel;

        private string _testlabel = "Gary";
        public string TestLabel
        {
            get {  return _testlabel; }
            set {
                _testlabel = value;
                OnPropertyChanged(nameof(TestLabel)); }}

        public ThingsDataContext(ThingsViewModel thingsViewModel)
        {
            _thingsViewModel = thingsViewModel;
            ThingModel thingModel = thingsViewModel.GetAllThings();
            _thingViewModel = new ThingViewModel(thingModel);
        }
    }
}
 
so my initial Code works to display the structure after i made some changes based on the posts you sent, so thank you
my next challenge is to add the check boxes

any suggestions?

thanks

Madaxe

xaml:
 <TreeView x:Name="MainTreeView" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" ItemsSource="{Binding ThingViewModels}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type interface:IThingViewModel}" ItemsSource="{Binding ViewModelChildren}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

Data Context:
public class ThingsDataContext:ViewModelBase
    {
        private ThingsViewModel _thingsViewModel;
        private List<ThingViewModel> _thingViewModel = new List<ThingViewModel>();
        public List<ThingViewModel> ThingViewModels => _thingViewModel;

        public ThingsDataContext(ThingsViewModel thingsViewModel)
        {
            _thingsViewModel = thingsViewModel;
            ThingModel thingModel = thingsViewModel.GetAllThings();
            _thingViewModel.Add(new ThingViewModel(thingModel));
        }
    }
 
Last Question, I've made good progress thank you. How do i return the object to the command?

thanks

Madaxe

TreeView.xaml:
<TreeView x:Name="MainTreeView" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" ItemsSource="{Binding ThingViewModels}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type interface:IThingViewModel}" ItemsSource="{Binding ViewModelChildren}">
                    <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="{Binding IsChecked}"
                                  Command="{Binding CheckboxCommand}"
                                  CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}"
                                  Margin="0 0 10 0"/>
                        <TextBlock Text="{Binding Name}"/>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
 
Currently im using "{Binding RelativeSource={RelativeSource Self}}" to send the CheckBox to the Command Class is there a better way to get the DataContext of the CheckBox directly?

Thanks

Madaxe

xaml.cs:
<CheckBox IsChecked="{Binding IsChecked}"
                                  Command="{Binding CheckboxCommand}"
                                  CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
                                  Margin="0 0 10 0"/>


Command.cs:
namespace treeview.Commands
{
    public class CheckboxCommand : CommandBase
    {
        public override void Execute(object parameter)
        {
            if (parameter is CheckBox)
            {
                CheckBox checkBox = (CheckBox)parameter;
                if(checkBox.DataContext is ThingViewModel)
                {
                    ThingsViewModel thingsViewModel = (ThingsViewModel)checkBox.DataContext;
                }
            }
        }
    }
}
 
Did you set breakpoints? Are the appropriate binding points being called?
 
I had to change some property's from late bound to property's with backers as the objects were being recreated. Hence your comment earlier. I now have the dialogue shown below I'm just working on the logic for check box states.

1645398264261.png


ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using treeview.Commands;
using treeview.Models;

namespace treeview.ViewModels
{
    public class ThingViewModel : ViewModelBase, IThingViewModel
    {
        ThingModel _thingModel;
        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get
            {
                return _isChecked;
            }
            set
            {
                _isChecked = value;
                if (_viewModelParent != null) { UpdateParentIsChecked(true); }               
                OnPropertyChanged(nameof(IsChecked));
            }
        }
        public bool AllChildrenChecked
        {
            get
            {
                if (ViewModelChildren != null)
                {
                    var y = from x in ViewModelChildren
                            where x.IsChecked == true
                            select x;
                    return (y.Count() == ViewModelChildren.Count()) ? true : false;
                }
                return false;
            }
        }
        public bool AllChildrenUnChecked
        {
            get
            {
                if (ViewModelChildren != null)
                {
                    var y = from x in ViewModelChildren
                            where x.IsChecked == true
                            select x;
                    return (y.Count() == 0) ? true : false;
                }
                return false;
            }
        }
        public bool IsInitiallySelected { get; set; }
        public ThingModel Parent => _thingModel.Parent;
        public List<ThingModel> Children => _thingModel.Children;
        public string Name => _thingModel.Name;

        private List<ThingViewModel> _viewModelChildren;
        public List<ThingViewModel> ViewModelChildren
        {
            get
            {
                if (_viewModelChildren == null)
                {
                    _viewModelChildren = GetViewModelChildren();
                    return _viewModelChildren;
                }
                return _viewModelChildren;
            }
        }

        private ThingViewModel _viewModelParent;
        public ThingViewModel ViewModelParent
        {
            get
            {
                if (_viewModelParent == null)
                {
                    _viewModelParent =  GetViewModelParent();
                    return _viewModelParent;
                }
                else
                {
                    return _viewModelParent;
                }
            }
        }
        public ICommand CheckboxCommand { get; }

        public Guid guid;
        public ThingViewModel(ThingModel thingModel)
        {
            _thingModel = thingModel;
            CheckboxCommand = new CheckboxCommand(this);
            guid = Guid.NewGuid();
        }
        public ThingViewModel(ThingModel thingModel, ThingViewModel thingViewModel)
        {
            _thingModel = thingModel;
            _viewModelParent = thingViewModel;
            CheckboxCommand = new CheckboxCommand(this);
            guid = Guid.NewGuid();
        }
        public void UpdateParentIsChecked(bool? isChecked)
        {
            ViewModelParent.IsChecked = isChecked;
        }
        public void CheckAllChildren()
        {
            IsChecked = null;
            if (ViewModelChildren != null)
            {
                foreach (ThingViewModel thingViewModel in ViewModelChildren)
                {
                    thingViewModel.IsChecked = true;
                    thingViewModel.CheckAllChildren();
                }
            }
        }
        private List<ThingViewModel> GetViewModelChildren()
        {
            List<ThingViewModel> thingViewModels = new List<ThingViewModel>();
            if (Children != null)
            {
                foreach (ThingModel thingModel in Children)
                {
                    thingViewModels.Add(new ThingViewModel(thingModel, this));
                }
                return thingViewModels;
            }
            return null;
        }
        private ThingViewModel GetViewModelParent()
        {
            return new ThingViewModel(Parent);
        }
    }
}
 
final working view model

ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using treeview.Commands;
using treeview.Models;

namespace treeview.ViewModels
{
    public class ThingViewModel : ViewModelBase, IThingViewModel
    {
        ThingModel _thingModel;
        
        public Guid guid;

        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get
            {
                return _isChecked;
            }
            set
            {
                _isChecked = value;
                OnPropertyChanged(nameof(IsChecked));
                OnPropertyChanged(nameof(State));
            }
        }

        public bool? AllChildrenChecked
        {
            get
            {
                if (ViewModelChildren != null)
                {
                    bool? returnBool = false;
                    bool? checkedBool = false;
                    var y = from x in ViewModelChildren
                            where x.IsChecked == true
                            select x;
                    checkedBool = (y.Count() == ViewModelChildren.Count()) ? true : false;

                    bool? nullBool = false;
                    var w = from z in ViewModelChildren
                            where z.IsChecked == null
                            select z;
                    nullBool = (w.Count() == ViewModelChildren.Count()) ? true : false;

                    returnBool = (nullBool == true) ? nullBool : checkedBool;

                    return returnBool;
                }
                return false;
            }
        }
        public bool? AllChildrenUnChecked
        {
            get
            {
                if (ViewModelChildren != null)
                {
                    bool? returnBool = false;
                    bool? uncheckedBool = false;

                    var y = from x in ViewModelChildren
                            where x.IsChecked == false
                            select x;
                    uncheckedBool=(y.Count() == ViewModelChildren.Count()) ? true : false;

                    bool? nullBool = false;
                    var w = from z in ViewModelChildren
                            where z.IsChecked == null
                            select z;
                    nullBool = (w.Count() == ViewModelChildren.Count()) ? true : false;

                    returnBool = (nullBool == false) ? uncheckedBool: nullBool;

                    return returnBool;
                }
                return false;
            }
        }
        public bool IsRoot { get; private set; } = false;
        public bool IsLeaf
        {
            get
            {
                return (HasChildren==false) ?true:false;
            }
        }
        public bool HasChildren
        {
            get
            {
                return (ViewModelChildren == null) ? false : true;
            }
        }
        public bool HasParent
        {
            get
            {
                return (ViewModelParent == null) ? false : true;
            }
        }

        public string State
        {
            get
            {
                if (IsChecked == null)
                {
                    return "Null";
                }
                else if (IsChecked == true)
                {
                    return "True";
                }
                else
                {
                    return "False";
                }
            }
        }
        public string Name => _thingModel.Name;

        public ThingModel Parent => _thingModel.Parent;
        public List<ThingModel> Children => _thingModel.Children;


        private List<ThingViewModel> _viewModelChildren = null;
        public List<ThingViewModel> ViewModelChildren
        {
            get
            {
                if (_viewModelChildren == null)
                {
                    _viewModelChildren = GetViewModelChildren();
                    return _viewModelChildren;
                }
                return _viewModelChildren;
            }
        }

        private ThingViewModel _viewModelParent = null;
        public ThingViewModel ViewModelParent
        {
            get
            {
                if (_viewModelParent == null)
                {
                    _viewModelParent = GetViewModelParent();
                    return _viewModelParent;
                }
                else
                {
                    return _viewModelParent;
                }
            }
        }
        public ICommand CheckboxCommand { get; }


        public ThingViewModel(ThingModel thingModel)
        {
            _thingModel = thingModel;
            CheckboxCommand = new CheckboxCommand(this);
            guid = Guid.NewGuid();
        }
        public ThingViewModel(ThingModel thingModel, bool isRoot)
        {
            _thingModel = thingModel;
            IsRoot = isRoot;
            CheckboxCommand = new CheckboxCommand(this);
            guid = Guid.NewGuid();
        }
        public ThingViewModel(ThingModel thingModel, ThingViewModel thingViewModel)
        {
            _thingModel = thingModel;
            _viewModelParent = thingViewModel;
            CheckboxCommand = new CheckboxCommand(this);
            guid = Guid.NewGuid();
        }
            
        
        public void UpdateParentIsChecked(bool? isChecked)
        {
            ViewModelParent.IsChecked = isChecked;
        }
        public void CheckAllChildren()
        {
            IsChecked = (HasChildren) ? null : true;
            if (HasChildren == true)
            {
                foreach (ThingViewModel thingViewModel in ViewModelChildren)
                {
                    thingViewModel.IsChecked = true;
                    thingViewModel.CheckAllChildren();
                }
            }
        }
        public void CheckAllParents()
        {
            if (HasParent == true && IsRoot == false)
            {
                ThingViewModel thingViewModel = ViewModelParent;

                if (ViewModelParent.GetViewModelChildren() != null)
                {
                    bool? State = (thingViewModel.AllChildrenChecked == true) ? null : true;
                    thingViewModel.IsChecked = State;
                    thingViewModel.CheckAllParents();
                }
            }
        }
        public void UnCheckAllChildren()
        {
            IsChecked = false;
            if (HasChildren == true)
            {
                foreach (ThingViewModel thingViewModel in ViewModelChildren)
                {
                    thingViewModel.IsChecked = false;
                    thingViewModel.UnCheckAllChildren();
                }
            }
        }
        public void UnCheckAllParents()
        {
            if (HasParent == true && IsRoot == false)
            {
                ThingViewModel thingViewModel = ViewModelParent;

                if (ViewModelParent.GetViewModelChildren() != null)
                {
                    bool? State = (thingViewModel.AllChildrenUnChecked == true) ? false : true;
                    if(thingViewModel.AllChildrenUnChecked == false)
                    {
                        State = true;
                    }
                    thingViewModel.IsChecked = State;
                    thingViewModel.UnCheckAllParents();
                }
            }
        }
        
        private List<ThingViewModel> GetViewModelChildren()
        {
            List<ThingViewModel> thingViewModels = new List<ThingViewModel>();
            if (Children != null)
            {
                foreach (ThingModel thingModel in Children)
                {
                    thingViewModels.Add(new ThingViewModel(thingModel, this));
                }
                return thingViewModels;
            }
            return null;
        }
        private ThingViewModel GetViewModelParent()
        {
            return new ThingViewModel(Parent);
        }
    }
}
 
Solution
Back
Top Bottom