WPF's TreeView is combined with CheckBox, and the parent node and child node affect each other

This example tossed me for several days, and finally got it today. Since it's too late, I won't talk about the process, just look at the results and code.

1. Traverse the folder: In this example, the CheckBox displays three states. In addition, the parent node is selected, the child nodes are all selected, the parent node is not selected, and the child nodes are not selected; the child node is partially selected, the parent node is in the null state; the child node is in the null state. Select all, the parent node is selected; unselect all child nodes, the parent node is not selected. This example is suitable for traversal.


Next, look at the code (for example, I forgot which god wrote this code, and I will attach a link and thank you next time I find it):

class DirectoryTree (a bug in the original code is dealt with here to automatically filter out unreadable folders)

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CheckBoxTreeView
{
    class DirectoryTree : INotifyPropertyChanged
    {
        public DirectoryTree()
        {
            this.isChecked = false;
        }
        public DirectoryTree(DirectoryTree parent, DirectoryInfo di)
            : this()
        {
            this.Parent = parent;
            this.Info = of;
        }
        public DirectoryTree(DirectoryTree parent, DirectoryInfo di, Boolean bChecked)
            : this(parent, di)
        {
            this.isChecked = bChecked;
        }
        public DirectoryInfo Info { get; set; }
        public DirectoryTree Parent { get; set; }
        public Boolean? IsChecked
        {
            get { return this.isChecked; }
            set
            {
                if (this.isChecked != value)
                {
                    this.isChecked = value;
                    NotifyPropertyChanged("IsChecked");
                    if (this.isChecked == true) // if the node is checked
                    {
                        if (this.dirs != null)
                            foreach (DirectoryTree dt in this.dirs)
                                dt.IsChecked = true;
                        if (this.Parent != null)
                        {
                            Boolean bExistUncheckedChildren = false;
                            foreach (DirectoryTree dt in this.Parent.Directories)
                                if (dt.IsChecked != true)
                                {
                                    bExistUncheckedChildren = true;
                                    break;
                                }
                            if (bExistUncheckedChildren)
                                this.Parent.IsChecked = null;
                            else
                                this.Parent.IsChecked = true;
                        }
                    }
                    else if (this.isChecked == false) // if the node is not checked
                    {
                        if (this.dirs != null)
                            foreach (DirectoryTree dt in this.dirs)
                                dt.IsChecked = false;
                        if (this.Parent != null)
                        {
                            Boolean bExistCheckedChildren = false;
                            foreach (DirectoryTree dt in this.Parent.Directories)
                                if (dt.IsChecked != false)
                                {
                                    bExistCheckedChildren = true;
                                    break;
                                }
                            if (bExistCheckedChildren)
                                this.Parent.IsChecked = null;
                            else
                                this.Parent.IsChecked = false;
                        }
                    }
                    else
                    {
                        if (this.Parent != null)
                            this.Parent.IsChecked = null;
                    }
                }
            }
        }
        public ObservableCollection<DirectoryTree> Directories
        {
            get
            {
                if (this.dirs == null)
                {
                    this.dirs = new ObservableCollection<DirectoryTree>();
                    try
                    {
                        foreach (DirectoryInfo di in Info.GetDirectories())
                            this.dirs.Add(new DirectoryTree(this, di, this.isChecked == true));
                    }
                    catch
                    {

                    }                    
                }
                return dirs;
            }
        }


        public static ObservableCollection<DirectoryTree> InitRoot()
        {
            ObservableCollection<DirectoryTree> dts = new ObservableCollection<DirectoryTree>();
            DriveInfo[] drvs = DriveInfo.GetDrives();
            foreach (DriveInfo drv in drvs)
            {
                if (drv.DriveType == DriveType.Fixed)
                {
                    DirectoryTree dt = new DirectoryTree(null, drv.RootDirectory);
                    dts.Add(dt);
                }
            }
            return dts;
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        private Boolean? isChecked;
        private ObservableCollection<DirectoryTree> dirs;
    }
}

Main window background code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CheckBoxTreeView
{
    /// <summary>
    /// Interaction logic of MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.tvDirectories.ItemsSource = DirectoryTree.InitRoot();
        }
    }
}

Main window foreground code:

<Window x:Class="CheckBoxTreeView.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:CheckBoxTreeView"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    
    <Grid>
        <TreeView Name="tvDirectories" Grid.Column="0" Margin="0,0,5,0">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="{x:Type local:DirectoryTree}" ItemsSource="{Binding Directories}">
                    <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="{Binding Path=IsChecked}" IsTabStop="False" Focusable="False" HorizontalAlignment="Center"/>                        
                        <TextBlock Text="{Binding Path=Info}" HorizontalAlignment="Center"/>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

The above is the code to traverse. But many times, we need a quantitative TreeView structure, here I slightly modify the above code, change it to a double-layer structure code, the same function. First look at the effect:


Then modify the code based on the above:

Node class:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TreeViewAndCheckboxTest
{
    public class Node : INotifyPropertyChanged
    {
        public Node()
        {
            this.isChecked = false;
        }
        public Node(Node parent,string nodeName):this()
        {
            Parent = parent;
            NodeName = nodeName;
        }
        public Node(Node parent, string nodeName, Boolean bChecked)
            : this(parent, nodeName)
        {
            this.isChecked = bChecked;
        }
        public Node Parent;

        public event PropertyChangedEventHandler PropertyChanged;       
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        private Boolean? isChecked;

        public Boolean? IsChecked
        {
            get { return this.isChecked; }
            set
            {
                if (this.isChecked != value)
                {
                    this.isChecked = value;
                    NotifyPropertyChanged("IsChecked");
                    if (this.isChecked == true) // if the node is checked
                    {
                        if (this.childNode != null)
                            foreach (Node dt in this.childNode)
                                dt.IsChecked = true;
                        if (this.Parent != null)
                        {
                            Boolean bExistUncheckedChildren = false;
                            foreach (Node dt in this.Parent.ChildNode)
                                if (dt.IsChecked != true)
                                {
                                    bExistUncheckedChildren = true;
                                    break;
                                }
                            if (bExistUncheckedChildren)
                                this.Parent.IsChecked = null;
                            else
                                this.Parent.IsChecked = true;
                        }
                    }
                    else if (this.isChecked == false) // if the node is not checked
                    {
                        if (this.childNode != null)
                            foreach (Node dt in this.childNode)
                                dt.IsChecked = false;
                        if (this.Parent != null)
                        {
                            Boolean bExistCheckedChildren = false;
                            foreach (Node dt in this.Parent.ChildNode)
                                if (dt.IsChecked != false)
                                {
                                    bExistCheckedChildren = true;
                                    break;
                                }
                            if (bExistCheckedChildren)
                                this.Parent.IsChecked = null;
                            else
                                this.Parent.IsChecked = false;
                        }
                    }
                    else
                    {
                        if (this.Parent != null)
                            this.Parent.IsChecked = null;
                    }
                }
            }
        }

        

        private ObservableCollection<Node> childNode;

        public string NodeName { get; set; }

        public ObservableCollection<Node> InitRoot()
        {
            ObservableCollection<Node> dts = new ObservableCollection<Node>();
            
            for(int i=0;i<2;i++)
            {                
                 Node dt = new Node(null, i.ToString()+"a");
                 dts.Add(dt);                
            }
            return dts;
        }

        public ObservableCollection<Node> ChildNode
        {
            get
            {
                if (this.childNode == null)
                {
                    this.childNode = new ObservableCollection<Node>();
                    if(NodeName.Equals("0a"))
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            this.childNode.Add(new Node(this, "b" + i.ToString(), this.isChecked == true));
                        }
                    }
                    else if(NodeName.Equals("1a"))
                    {
                        for (int i = 0; i < 4; i++)
                        {
                            this.childNode.Add(new Node(this, "c" + i.ToString(), this.isChecked == true));
                        }
                    }                    
                }
                return childNode;
            }
        }
    }
}

Main window background code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TreeViewAndCheckboxTest
{
    /// <summary>
    /// Interaction logic of MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.tvDirectories.ItemsSource = new Node().InitRoot();
        }
    }
}

Main window foreground code:

<Window x:Class="TreeViewAndCheckboxTest.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:TreeViewAndCheckboxTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <TreeView Name="tvDirectories" Grid.Column="0" Margin="0,0,5,0">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding ChildNode}">
                <StackPanel Orientation="Horizontal">
                    <CheckBox IsChecked="{Binding Path=IsChecked}" IsTabStop="False" Focusable="False" HorizontalAlignment="Center"/>
                    <TextBlock Text="{Binding Path=NodeName}" HorizontalAlignment="Center"/>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</Window>
This perfectly implements the tree structure that needs to be traversed, and the two forms of code that make up the tree structure by itself. As for the formation of more cool functions, you only need to combine various styles and Templates to use them.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325852352&siteId=291194637