文章最后给出了源码下载地址
图1
图2 
模版加上了一个checkbox,选中父类的checkbox 所有的子类也相就选中。
如果子类没有全部父类的checkbox不会选中
用vmmm我们要先实现INotifyPropertyChanged
///
<summary>//////
</summary>public class NotifyPropertyBase
: INotifyPropertyChanged{ #region
INotifyPropertyChanged public void OnPropertyChanged(string propertyName) { if (PropertyChanged
!= null) { PropertyChanged(this,new PropertyChangedEventArgs(propertyName)); } } public event PropertyChangedEventHandler
PropertyChanged; #endregion}为了避免硬编码错误我写一个扩展方法
///
<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); } } #endregion |
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.Bodyis MemberExpression) { _propertyName
= (expr.Body as MemberExpression).Member.Name; } else if (expr.Bodyis UnaryExpression) { _propertyName
= ((expr.Body as UnaryExpression).Operandas MemberExpression).Member.Name; } return _propertyName; } }
///
<summary> ///
因为用到泛型了不能写成abstract 类 /// ///
</summary> public class MyTree
: NotifyPropertyBase { #region
父 public MyTree
Parent { get; set; } #endregion #region
子 public List<MyTree>
Children { get; set; } #endregion #region
节点的名字 public string Name { get; set; } #endregion #region
Constructors public MyTree(string name) { this.Name=name; this.Children=new List<MyTree>(); } public MyTree()
{ } public #endregion #region
CheckBox是否选中 bool?
_isChecked; public bool?
IsChecked { get { return _isChecked; } set { SetIsChecked(value,true,true); } } 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() { 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); } #endregion #region
选中的行 IsSelected bool _isSelected; public bool IsSelected { get { return _isSelected; } set { _isSelected
= value; this.SetProperty(x
=> x.IsChecked); if (_isSelected) { SelectedTreeItem
= this; MessageBox.Show("选中的是" +
SelectedTreeItem.Name); } else SelectedTreeItem
= null; } } #endregion #region
选中的数据 public MyTree
SelectedTreeItem { get; set; } #endregion #region
创建树 public void CreateTreeWithChildre(
MyTree children,bool?
isChecked) { this.Children.Add(children); children.Parent
= this; children.IsChecked
= isChecked; } #endregion }
我们再下面实现ViewModel
public class TreeViewModel:NotifyPropertyBase { public List<MyTree>
MyTrees { get; set; } public TreeViewModel() { MyTrees
= new List<MyTree>(); MyTrees.Add(MyCreateTree()); } ///
<summary> ///
创建树 ///
</summary> ///
<returns></returns> public MyTree
MyCreateTree() { MyTree
_myT = new MyTree("中国"); #region
北京 MyTree
_myBJ = new MyTree("北京"); _myT.CreateTreeWithChildre(_myBJ,false); MyTree
_HD = new MyTree("海淀区"); MyTree
_CY = new MyTree("朝阳区"); MyTree
_FT = new MyTree("丰台区"); MyTree
_DC = new MyTree("东城区"); _myBJ.CreateTreeWithChildre(_HD,false); _HD.CreateTreeWithChildre(new MyTree("某某1"),false); _HD.CreateTreeWithChildre(new MyTree("某某2"),true); _myBJ.CreateTreeWithChildre(_CY,false); _myBJ.CreateTreeWithChildre(_FT,false); _myBJ.CreateTreeWithChildre(_DC,false); #endregion #region
河北 MyTree
_myHB = new MyTree("河北"); _myT.CreateTreeWithChildre(_myHB,false); MyTree
_mySJZ = new MyTree("石家庄"); MyTree
_mySD = new MyTree("山东"); MyTree
_myTS = new MyTree("唐山"); _myHB.CreateTreeWithChildre(_mySJZ,true); _myHB.CreateTreeWithChildre(_mySD,false); _myHB.CreateTreeWithChildre(_myTS,false); #endregion return _myT; } }
<Window x:Class="MyWpfCheckTreeDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:MyWpfCheckTreeDemo.AppViewModel"
Title="MainWindow" Height="350" Width="525" Loaded="LoadedEvent">
<Window.Resources>
<HierarchicalDataTemplate x:Key="MyTreeItemTemplate" DataType="{x:Type VM:MyTree}" ItemsSource="{Binding Path=Children,Mode=OneWay}">
<StackPanel x:Name="My_SP" Orientation="Horizontal" Margin="2">
<CheckBox IsChecked="{Binding Path=IsChecked}" >
</CheckBox>
<ContentPresenter Content="{Binding Path=Name,Mode=OneTime}" Margin="2,0"/>
</StackPanel>
</HierarchicalDataTemplate>
<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsSelected" Value="{Binding Path=IsSelected,Mode=TwoWay}"/>
</Style>
</Window.Resources>
<Grid>
<DockPanel>
<TreeView x:Name="tv" ItemsSource="{Binding MyTrees}"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemTemplate="{StaticResource MyTreeItemTemplate}"
></TreeView>
</DockPanel>
</Grid>
</Window>
本文介绍如何在WPF中实现树形视图(TreeView)的复选框功能,包括父节点与子节点之间的状态同步。通过定义自定义类`MyTree`和视图模型`TreeViewModel`,实现父节点全选则所有子节点被选中,子节点全部选中时父节点也被选中。此外,还展示了如何通过XAML模板实现视图的定制。
929

被折叠的 条评论
为什么被折叠?



