工作中要為很多類創建TreeView, 很多時候僅僅是因為要顯示字段不同, 就得Ctrl+C、Ctrl+V復制一份幾乎相同的代碼, 這難免讓人生厭, 於是希望像泛型集合的擴展方法那樣, 可以在使用的時候靈活指定要顯示哪個字段.
下面的TreeView要實現這樣的邏輯: 父項目 被勾選 或者 取消勾選, 那么它的所有子項目全部改成 被勾選 或者 取消勾選; 只有所有的子項目都被勾選時, 父項目才自發的改成被勾選
- 創建基本的CommonTreeViewItemModel
/// <summary> /// 通用的TreeViewItem模型, 僅包含最基礎CheckBox(如果你覺得不好看, 在CommonTreeView.xaml中修改樣式), 還包含一個 Tag(包含的對象) /// 業務邏輯: 父項目 被勾選 或者 取消勾選, 那么它的所有子項目全部改成 被勾選 或者 取消勾選; 只有所有的子項目都被勾選時, 父項目才自發的改成被勾選 /// 所有的字段都改為protected, 方便繼承修改 /// </summary> public class CommonTreeViewItemModel : INotifyPropertyChanged { #region 屬性 public event PropertyChangedEventHandler PropertyChanged; protected object id; /// <summary> /// 唯一性Id /// </summary> public object Id { get { return id; } set { id = value; } } protected string caption; /// <summary> /// 標題 /// </summary> public string Caption { get { return caption; } set { caption = value; if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Caption")); } } protected bool isChecked; /// <summary> /// 是否被勾選 /// </summary> public bool IsChecked { get { return isChecked; } set { isChecked = value; if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsChecked")); SetIsCheckedByParent(value); if (Parent != null) Parent.SetIsCheckedByChild(value); } } protected bool isExpanded; /// <summary> /// 是否被展開 /// </summary> public bool IsExpanded { get { return isExpanded; } set { isExpanded = value; if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsExpanded")); } } protected CommonTreeViewItemModel parent; /// <summary> /// 父項目 /// </summary> public CommonTreeViewItemModel Parent { get { return parent; } set { parent = value; } } protected List<CommonTreeViewItemModel> children = new List<CommonTreeViewItemModel>(); /// <summary> /// 含有的子項目 /// </summary> public List<CommonTreeViewItemModel> Children { get { return children; } set { children = value; } } /// <summary> /// 包含的對象 /// </summary> public object Tag { get; set; } /// <summary> /// 包含對象的類型 /// </summary> public Type TagType { get; set; } #endregion #region 業務邏輯, 如果你需要改成其他邏輯, 要修改的也就是這兩行 /// <summary> /// 子項目的isChecked改變了, 通知 是否要跟着改變 isChecked /// </summary> /// <param name="value"></param> public virtual void SetIsCheckedByChild(bool value) { if (this.isChecked == value) { return; } bool isAllChildrenChecked = this.Children.All(c => c.IsChecked == true); this.isChecked = isAllChildrenChecked; if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsChecked")); if (Parent != null) Parent.SetIsCheckedByChild(value); } /// <summary> /// 自己的isChecked改變了, 所有子項目都要跟着改變 /// </summary> /// <param name="value"></param> public virtual void SetIsCheckedByParent(bool value) { this.isChecked = value; if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsChecked")); foreach (var child in Children) { child.SetIsCheckedByParent(value); } } #endregion }
- 創建通用的 CommonTreeView, 這其中最關鍵的就是泛型方法SetItemsSourceData<TSource, TId>
public partial class CommonTreeView : UserControl { public IList<CommonTreeViewItemModel> ItemsSourceData { get { return (IList<CommonTreeViewItemModel>)innerTree.ItemsSource; } } public CommonTreeView() { InitializeComponent(); } /// <summary> /// 設置數據源, 以及各個字段 /// </summary> /// <typeparam name="TSource">數據源類型</typeparam> /// <typeparam name="TId">主鍵類型</typeparam> /// <param name="itemsArray">數據源列表</param> /// <param name="captionSelector">指定顯示為Caption的屬性</param> /// <param name="idSelector">指定主鍵屬性</param> /// <param name="parentIdSelector">指定父項目主鍵屬性</param> public void SetItemsSourceData<TSource, TId>(IEnumerable<TSource> itemsArray, Func<TSource, string> captionSelector, Func<TSource, TId> idSelector, Func<TSource, TId> parentIdSelector) where TId : IEquatable<TId> { var list = new List<CommonTreeViewItemModel>(); foreach (var item in itemsArray.Where(a => object.Equals(default(TId), parentIdSelector(a)))) { var tvi = new CommonTreeViewItemModel(); tvi.Caption = captionSelector(item).ToString(); tvi.Id = idSelector(item); tvi.Tag = item; tvi.TagType = item.GetType(); list.Add(tvi); RecursiveAddChildren(tvi, itemsArray, captionSelector, idSelector, parentIdSelector); } innerTree.ItemsSource = list; return; } /// <summary> /// 遞歸加載children /// </summary> /// <typeparam name="TSource"></typeparam> /// <typeparam name="TId"></typeparam> /// <param name="parent"></param> /// <param name="itemsArray"></param> /// <param name="captionSelector"></param> /// <param name="idSelector"></param> /// <param name="parentIdSelector"></param> /// <returns></returns> private CommonTreeViewItemModel RecursiveAddChildren<TSource, TId>(CommonTreeViewItemModel parent, IEnumerable<TSource> itemsArray, Func<TSource, string> captionSelector, Func<TSource, TId> idSelector, Func<TSource, TId> parentIdSelector) { foreach (var item in itemsArray.Where(a => parent.Id.Equals(parentIdSelector(a)))) { var tvi = new CommonTreeViewItemModel(); tvi.Caption = captionSelector(item); tvi.Id = idSelector(item); tvi.Tag = item; tvi.TagType = item.GetType(); tvi.Parent = parent; parent.Children.Add(tvi); RecursiveAddChildren(tvi, itemsArray, captionSelector, idSelector, parentIdSelector); } return parent; }
- CommonTreeView 對應的xaml
<UserControl x:Class="WPFCommonTreeView.CommonTreeView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WPFCommonTreeView" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <TreeView Name="innerTree"> <TreeView.ItemContainerStyle> <Style TargetType="TreeViewItem"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"></Setter> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:CommonTreeViewItemModel}" ItemsSource="{Binding Children}"> <CheckBox FontSize="14" FontFamily="微軟雅黑" Tag="{Binding Children}" IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Caption}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </UserControl>
- 演示效果
后記: 說實在的, 這個通用TreeView其實一點也不通用, 簡單倒是真的, 主要原因是實際業務中TreeView的層次和邏輯千差萬別, 就當自己的一點嘗試吧.
轉載請注明出處: http://www.cnblogs.com/zhouandke/p/6201064.html