簡單的通用TreeView(WPF)


      工作中要為很多類創建TreeView, 很多時候僅僅是因為要顯示字段不同, 就得Ctrl+C、Ctrl+V復制一份幾乎相同的代碼, 這難免讓人生厭, 於是希望像泛型集合的擴展方法那樣, 可以在使用的時候靈活指定要顯示哪個字段.

       下面的TreeView要實現這樣的邏輯: 父項目 被勾選 或者 取消勾選, 那么它的所有子項目全部改成 被勾選 或者 取消勾選; 只有所有的子項目都被勾選時, 父項目才自發的改成被勾選

  1. 創建基本的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
    
        }
  2. 創建通用的 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;
            }
  3. 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>
  4. 演示效果
    QQ拼音截圖未命名

 

        實例代碼下載

        后記: 說實在的, 這個通用TreeView其實一點也不通用, 簡單倒是真的, 主要原因是實際業務中TreeView的層次和邏輯千差萬別, 就當自己的一點嘗試吧.

       

         轉載請注明出處: http://www.cnblogs.com/zhouandke/p/6201064.html

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM