这个例子折腾了我好几天,今天终于搞定了,由于太晚了,就不说过程,只看结果和代码。
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组合起来用即可。