WPF的TreeView与CheckBox结合,父节点和子节点相互影响

这个例子折腾了我好几天,今天终于搞定了,由于太晚了,就不说过程,只看结果和代码。

1、遍历文件夹:在这个例子中,CheckBox显示三种状态,另外父节点选中,子节点全选,父节点不选,子节点全不选;子节点部分选,父节点null状态;子节点全选,父节点选中状态;子节点全不选,父节点不选状态。这个例子适合遍历。


接下来看代码(这个代码的例子忘记了是哪个大神写的,下次找到后附链接加感谢):

class DirectoryTree(这里处理了原来的代码的一个bug,从而自动过滤掉不能读取的文件夹)

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 = di;
        }
        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 (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 (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;
    }
}

主窗口后台代码:

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>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.tvDirectories.ItemsSource = DirectoryTree.InitRoot();
        }
    }
}

主窗口前台代码:

<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>

以上是遍历的代码。但是好多时候,我们需要定量的TreeView结构,这里我稍微修改一下上面的代码,改成双层结构的代码,同样的功能。先来看效果:


然后在上面的基础上修改代码:

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 (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 (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;
            }
        }
    }
}

主窗口后台代码:

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>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.tvDirectories.ItemsSource = new Node().InitRoot();
        }
    }
}

主窗口前台代码:

<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>
这样就完美实现了需要遍历的树结构,和自己组成树结构的两种形式的代码。至于形成更炫酷的功能,就只需要各种style和Template组合起来用即可。

猜你喜欢

转载自blog.csdn.net/niuge8905/article/details/80058166