因工作需要,自定义一个树形控件,支持多选并且父节点选中子节点全部选中,反之全部取消
<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:VM="clr-namespace:treeview"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" >
<Grid.Resources>
<Style TargetType="StackPanel" x:Key="ToolTipStyle">
<Style.Triggers>
<Trigger Property="ToolTip" Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
<HierarchicalDataTemplate x:Key="MyTreeItemTemplate"
DataType="{x:Type sctrls:CommonTreeView}"
ItemsSource="{Binding Path=Children,Mode=OneWay}">
<StackPanel x:Name="My_SP" Style="{StaticResource ResourceKey=ToolTipStyle}" Orientation="Horizontal" Margin="2" ToolTip="{Binding NodeTag}">
<CheckBox IsChecked="{Binding Path=IsChecked}" >
</CheckBox>
<TextBlock>
<ContentPresenter Content="{Binding Path=NodeName,Mode=OneTime}" Margin="2,0"/>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="False" />
</Style>
</Grid.Resources>
<Grid >
<TreeView Grid.Row="1" x:Name="tv" ItemsSource="{Binding MyTrees}"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemTemplate="{StaticResource MyTreeItemTemplate}">
</TreeView>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Text;
namespace treeview
{
public class NotifyPropertyBase : INotifyPropertyChanged
{
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
/// <summary>
/// 扩展方法
/// 避免硬编码问题
/// </summary>
public static class NotifyPropertyBaseEx
{
public static void SetProperty<T, U>(this T tvm, Expression<Func<T, U>> expre) where T : NotifyPropertyBase, new()
{
string _pro = CommonFun.GetPropertyName(expre);
tvm.OnPropertyChanged(_pro);
}//为什么扩展方法必须是静态的
}
public class CommonFun
{
/// <summary>
/// 返回属性名
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="U"></typeparam>
/// <param name="expr"></param>
/// <returns></returns>
public static string GetPropertyName<T, U>(Expression<Func<T, U>> expr)
{
string _propertyName = "";
if (expr.Body is MemberExpression)
{
_propertyName = (expr.Body as MemberExpression).Member.Name;
}
else if (expr.Body is UnaryExpression)
{
_propertyName = ((expr.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
}
return _propertyName;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace treeview
{
public class CommonTreeView : NotifyPropertyBase
{
/// <summary>
/// 父
/// </summary>
public CommonTreeView Parent
{
get;
set;
}
/// <summary>
/// 子
/// </summary>
public List<CommonTreeView> Children
{
get;
set;
}
/// <summary>
/// 节点的名字
/// </summary>
public string NodeName
{
get;
set;
}
public bool? _isChecked;
/// <summary>
/// CheckBox是否选中
/// </summary>
public bool? IsChecked
{
get
{
return _isChecked;
}
set
{
SetIsChecked(value, true, true);
}
}
public CommonTreeView(string name)
{
this.NodeName = name;
this.Children = new List<CommonTreeView>();
}
public CommonTreeView() { }
private void SetIsChecked(bool? value, bool checkedChildren, bool checkedParent)
{
if (_isChecked == value) return;
_isChecked = value;
//选中和取消子类
if (checkedChildren && value.HasValue && Children != null)
Children.ForEach(ch => ch.SetIsChecked(value, true, false));
//选中和取消父类
if (checkedParent && this.Parent != null)
this.Parent.CheckParentCheckState();
//通知更改
this.SetProperty(x => x.IsChecked);
}
/// <summary>
/// 检查父类是否选 中
/// 如果父类的子类中有一个和第一个子类的状态不一样父类ischecked为null
/// </summary>
private void CheckParentCheckState()
{
List<CommonTreeView> checkedItems = new List<CommonTreeView>();
string checkedNames = string.Empty;
bool? _currentState = this.IsChecked;
bool? _firstState = null;
for (int i = 0; i < this.Children.Count; i++)
{
bool? childrenState = this.Children[i].IsChecked;
if (i == 0)
{
_firstState = childrenState;
}
else if (_firstState != childrenState)
{
_firstState = null;
}
}
if (_firstState != null) _currentState = _firstState;
SetIsChecked(_firstState, false, true);
}
/// <summary>
/// 创建树
/// </summary>
/// <param name="children"></param>
/// <param name="isChecked"></param>
public void CreateTreeWithChildre(CommonTreeView children, bool? isChecked)
{
this.Children.Add(children);
//必须先把孩子加入再为Parent赋值,
//否则当只有一个子节点时Parent的IsChecked状态会出错
children.Parent = this;
children.IsChecked = isChecked;
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace treeview
{
public class CommonTreeViewModel
{
public ObservableCollection<CommonTreeView> myTrees = new ObservableCollection<CommonTreeView>();
public CommonTreeViewModel() {
CommonTreeView _myy = new CommonTreeView("CCP");
MyTrees.Add(_myy);
CommonTreeView _myy1 = new CommonTreeView("CCP_1");
_myy.CreateTreeWithChildre(_myy1, true);
CommonTreeView _myy1_1 = new CommonTreeView("CCP_1.1");
_myy1.CreateTreeWithChildre(_myy1_1, true);
CommonTreeView _myy1_2 = new CommonTreeView("CCP_1.1");
_myy1.CreateTreeWithChildre(_myy1_2, true);
CommonTreeView _myy2 = new CommonTreeView("CCP_1");
_myy.CreateTreeWithChildre(_myy2, true);
_myy2.CreateTreeWithChildre(_myy1_1, true);
_myy2.CreateTreeWithChildre(_myy1_2, true);
}
public ObservableCollection<CommonTreeView> MyTrees
{
get { return myTrees; }
set { myTrees = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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 treeview
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
CommonTreeViewModel model = new CommonTreeViewModel();
this.tv.DataContext = model;
}
}
}
需要注意的是
1、 集合必须使用ObservableCollection,因为ObservableCollection继承了INotifyPropertyChanged,集合改变时会自动通知改变页面
2、集合MyTrees必须实现Get,Set方法 因为在C#中,写了{get;set;}才是属性,不写就是字段。而Binding的Source是对象,Path必须是属性。