工作中要为很多类创建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. 演示效果

 

        实例代码下载

        后记: 说实在的, 这个通用TreeView其实一点也不通用, 简单倒是真的, 主要原因是实际业务中TreeView的层次和逻辑千差万别, 就当自己的一点尝试吧.

       

         转载请注明出处: http://www.cnblogs.com/zhouandke/p/6201064.html

 

 

 

 

 

 

简单的通用TreeView(WPF)的更多相关文章

  1. [译]聊聊C#中的泛型的使用(新手勿入) Seaching TreeVIew WPF 可编辑树Ztree的使用(包括对后台数据库的增删改查) 字段和属性的区别 C# 遍历Dictionary并修改其中的Value 学习笔记——异步 程序员常说的「哈希表」是个什么鬼?

    [译]聊聊C#中的泛型的使用(新手勿入)   写在前面 今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,因此加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,当然在翻译的过程中发 ...

  2. 一个简单的通用Makefile实现

    一个简单的通用Makefile实现   Makefile是Linux下程序开发的自动化编译工具,一个好的Makefile应该准确的识别编译目标与源文件的依赖关系,并且有着高效的编译效率,即每次重新ma ...

  3. wpf中,一个简单的自定义treeview

    首先创建一个自定义控件,在里面定义好treeview的样式,将本来的三角形的图标变为加号的图标,并且添加节点之间的连线. <UserControl x:Class="TreeViewE ...

  4. 微信小程序实现简单的树形图treeview

    H5有很多树形图(树状图)的组件,echarts也有.比如像bootstrap的treeview,定制性很强.不过这些都无法方便地为小程序所用,除非整个页面用H5搭建再用webview框进去,有点粗暴 ...

  5. Seaching TreeVIew WPF

    项目中有一个树形结构的资源,需要支持搜索功能,搜索出来的结果还是需要按照树形结构展示,下面是简单实现的demo. 1.首先创建TreeViewItem的ViewModel,一般情况下,树形结构都包含D ...

  6. C#简单的通用分页

    通用分页技术分析 需要返回不同的类型的数据--采用泛型实现该操作 需要提供不同的方法 上一页 上一页 第一页 最后一页 跳转到指定页 Demo 代码 using System; using Syste ...

  7. Task(TPL)简单的实现Winform(WPF)异步

    很多时候,我们要实现Winform异步操作,你可以用传统的方法,但个人感觉代码不好理解,而且使用真有点不舒服.也可以用Task来实现,Task(.net4.0新添加的对象)其实就是对线程池线程的一个封 ...

  8. flowable笔记 - 简单的通用流程

    简介 通用流程可以用于一些基本的申请,例如请假.加班. 大致过程是: 1. 创建申请 2. 分配给审批人(需要审批人列表,当前审批人) -> 有下一个审批人 -> 3 -> 无 -& ...

  9. 简单的介绍下WPF中的MVVM框架

    最近在研究学习Swift,苹果希望它迅速取代复杂的Objective-C开发,引发了一大堆热潮去学它,放眼望去各个培训机构都已打着Swift开发0基础快速上手的招牌了.不过我觉得,等同于无C++基础上 ...

随机推荐

  1. 将声音文件加入VC

    概述 VC++是微软公司开发的一个IDE(集成开发环境),换句话说,就是使用c++的一个开发平台.有些软件就是这个编出来的...另外还有VB,VF.只是使用不同语言...但是,VC++是Windows ...

  2. Java堆内存的十个要点

    Java中的堆空间是什么? 当Java程序开始运行时,JVM会从操作系统获取一些内存.JVM使用这些内存,这些内存的一部分就是堆内存.堆内存通常在存储地址的底层,向上排列.当一个对象通过new关键字或 ...

  3. 头像上传,拖拽,裁切 (非HTML5)版本

    演示地址: http://codeman35.itongyin.com:19002/v2/web_demo.html 功能: 支持滚轴放大缩小,鼠标拖动,裁切可视区域,裁切和图片处理都是后端操作.

  4. ATL中窗口句柄与窗口过程的关联方法

    ATL中采用了一种动态生成机器指令的方式进行窗口句柄与窗口对象进行关联,以是详细分析: CWindowImpl会在第一次调用Create时注册窗口类,该窗口类是的信息是在CWindowImpl的子类中 ...

  5. html5 placeholder ie 不兼容问题 解决方案

    解决HTML5 placeholder的方案 来源:   时间:2013-09-05 20:06:49   阅读数:11375 分享到: 0 [导读] 使低版本浏览器支持Placeholder有很多方 ...

  6. windows7 编译boost1.54

    先去下载boost1.54 for windows原码.原来有个安装工具现在已经停止维护了,我试了旧版,已经安装不了. 这是它们的网站:http://www.boostpro.com/download ...

  7. disconf系列【2】——解决zk部署情况为空的问题

    如下图所示,在安装完成之后,发现zk(zookeeper)部署情况为空. 注:承接上篇,环境未发生改变. 1.解决zk没有启动的问题 查看disconf日志,发现zk没有启动. 实际情况是:zk已经启 ...

  8. XML文件中CDATA的作用

    操作XML文件时,如果允许用户输入内容,例如∶"< ".">"."/".""等,当生成XML时,会破坏了XM ...

  9. iOS GCD, 同步,异步,串行队列,并行队列,dispatch_group

    同步,指代码在同一个线程运行 异步,代码在另一个线程运行 串行队列,提交到该队列的block会顺序执行 并行队列,提交到该队列的block会并发执行 如果想等某一队列中所有block都执行完了在执行一 ...

  10. S5PV210的电阻触摸屏&ADC控制器

    一.ADC与触摸屏控制器结构框图 1.S5PV210一共支持10路模拟输入,分别为AIN0-AIN9.其中AIN0和AIN1是只做模拟输入的,AIN2-AIN9分别可以支持2个电阻式触摸屏,所以这个就 ...