zoukankan      html  css  js  c++  java
  • 简单的通用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

     

  • 相关阅读:
    AnaConda环境下安装librosa包超时
    [浙江大学数据结构]多项式求值,及算法效率问题
    java正则表达式测试用例
    tK Mybatis 通用 Mapper 3.4.6: Example 新增 builder 模式的应用
    Detect image format in Java(使用java探测图片文件格式)
    使用ColumnType注解解决/过滤/转义tk mybatis插入insertSelective、insert语句中遇到sql关键字
    IDEA中关闭sonar代码质量检测
    pip设置安装源
    无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系
    sql 查出一张表中重复的所有记录数据
  • 原文地址:https://www.cnblogs.com/zhouandke/p/6201064.html
Copyright © 2011-2022 走看看