今天有點時間,做個小例子WPF MVVM 實現TreeView 只是一個思路大家可以自由擴展
文章最后給出了源碼下載地址
圖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.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;
}
}
下面我們就來實現treeveivew的綁定類
/// <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;
}
}
我們再實現一個TreeView的模版
<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>
