1、运行效果:

2、前端代码

 <UserControl x:Class="iPIS.UI.Base.Tree.VideoTreeControl"
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:iPIS.UI.Base.Tree"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="80">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/iPIS.UI.Themes.Black;component/Base/Tree/VideoTreeControlImages.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBlock" x:Key="fontstyle">
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlFontColor}"></Setter>
<Setter Property="FontSize" Value="14"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<TreeView x:Name="tree"
AllowDrop="True"
ItemsSource="{Binding DataList}"
Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlBackground}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Grid x:Name="grid"
Margin="-1 0 0 0"
Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlBackground}"
>
<StackPanel Grid.Column="1"
Orientation="Horizontal"
Cursor="Hand"
Height="40"
VerticalAlignment="Center"
>
<Image x:Name="icon" Width="19" Height="16" VerticalAlignment="Center" Margin="0 0 5 0"></Image>
<TextBlock Text="{Binding Text}" Style="{StaticResource fontstyle}"></TextBlock>
<StackPanel x:Name="cc"
Orientation="Horizontal"
Visibility="Collapsed"
Margin="0 0 -1 0"
Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlBackground}"
>
<TextBlock Style="{StaticResource fontstyle}">(</TextBlock>
<TextBlock Text="{Binding FileCount}" Style="{StaticResource fontstyle}"></TextBlock>
<TextBlock Style="{StaticResource fontstyle}">)</TextBlock>
</StackPanel>
</StackPanel>
<!--模拟通行下划线-->
<Canvas>
<Line X1="0" Y1="39" X2="1000" Y2="39" Stroke="#4a4a4a" Margin="-500 0 0 0" StrokeThickness="1"></Line>
</Canvas>
</Grid>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Type}" Value="-1">
<Setter TargetName="icon" Property="Source" Value="{StaticResource icon_default}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="0">
<Setter TargetName="icon" Property="Source" Value="{StaticResource icon_yuan}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="1">
<Setter TargetName="icon" Property="Source" Value="{StaticResource icon_pian}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="2">
<Setter TargetName="icon" Property="Source" Value="{StaticResource icon_xulie}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="-1">
<Setter TargetName="cc" Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</UserControl>

3、控件的后台代码

 using iPIS.UI.Base.Model;
using iPIS.UI.Base.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; namespace iPIS.UI.Base.Tree
{
/// <summary>
/// VideoTreeControl.xaml 的交互逻辑
/// </summary>
public partial class VideoTreeControl : UserControl
{
public VideoTreeControl()
{
InitializeComponent();
this.DataContext = new VideoTreeControlViewModel();
//默认样式
VideoTreeControlBackground = "#36353a";
VideoTreeControlFontColor = "#9a9a9a"; tree.SelectedItemChanged += Tree_SelectedItemChanged;
tree.MouseDoubleClick += Tree_MouseDoubleClick;
} /// <summary>
/// 上下文
/// </summary>
VideoTreeControlViewModel vm
{
get
{
return this.DataContext as VideoTreeControlViewModel;
}
} /// <summary>
/// 背景颜色
/// </summary>
public string VideoTreeControlBackground
{
get { return (string)GetValue(VideoTreeControlBackgroundProperty); }
set { SetValue(VideoTreeControlBackgroundProperty, value); }
} /// <summary>
/// 字体颜色
/// </summary>
public string VideoTreeControlFontColor
{
get { return (string)GetValue(VideoTreeControlFontColorProperty); }
set { SetValue(VideoTreeControlFontColorProperty, value); }
} #region 附加属性 public static readonly DependencyProperty VideoTreeControlFontColorProperty =
DependencyProperty.Register("VideoTreeControlFontColor", typeof(string), typeof(VideoTreeControl), new PropertyMetadata("")); public static readonly DependencyProperty VideoTreeControlBackgroundProperty =
DependencyProperty.Register("VideoTreeControlBackground", typeof(string), typeof(VideoTreeControl), new PropertyMetadata(""));
#endregion #region 公开事件
/// <summary>
/// 双击节点
/// </summary>
public event EventHandler<VideoTreeControlItemModel> DoubleClickTreeItem; /// <summary>
/// 节点选中变更
/// </summary>
public event EventHandler<VideoTreeControlItemModel> SelectedItemChanged;
#endregion /// <summary>
/// 选中时
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Tree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var datamodel = e.NewValue as VideoTreeControlItemModel;
if (datamodel.Type == VideoTreeControlItemType.Root) return;//过滤root
SelectedItemChanged?.Invoke(vm, datamodel);
//获取方式 e.Data.GetData(typeof(Base.Model.VideoTreeControlItemModel))
DragDrop.DoDragDrop(sender as FrameworkElement, datamodel, DragDropEffects.Copy); /*
DragDrop.DoDragDrop(sender as FrameworkElement, new ImageTreeControlImageModel()
{
Text = "我是拖进来的",
Type = ImageTreeControlImageType.AlreadyMatting,
ImageSource = new BitmapImage(new Uri("D:\\上传资源\\xxx.png", UriKind.Absolute))
}, DragDropEffects.Copy);
*/
} /// <summary>
/// 双击节点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Tree_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var datamodel = tree.SelectedValue as VideoTreeControlItemModel;
if (datamodel == null || datamodel.Type == VideoTreeControlItemType.Root) return;//过滤root
DoubleClickTreeItem?.Invoke(vm, datamodel);
}
}
}

4、控件的datacontext对象

 using iPIS.UI.Base.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input; namespace iPIS.UI.Base.ViewModel
{
public class VideoTreeControlViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged; private ObservableCollection<VideoTreeControlItemModel> _DataList; /// <summary>
/// 数据源
/// </summary>
public ObservableCollection<VideoTreeControlItemModel> DataList
{
get => _DataList;
set
{
//检查是否有roo,不存在生成默认root
if (value.Where(c => c.Type == VideoTreeControlItemType.Root).Count() == )
{
_DataList = new ObservableCollection<VideoTreeControlItemModel>() {
new VideoTreeControlItemModel(){
Text = "默认",
Children = value,
Type = VideoTreeControlItemType.Root//标示为root
}
};
}
else
_DataList = value;
PropertyChanged?.Notify(() => this.DataList);
}
}
}
}

5、数据实体

 using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace iPIS.UI.Base.Model
{
/// <summary>
/// 视频图片树状控件,数据映射实体
/// </summary>
public class VideoTreeControlItemModel
{
private string _id = string.Empty;
/// <summary>
/// 唯一标示符
/// </summary>
public string Id
{
get
{
//如果调用者未显示给入唯一标识符,将默认生成一个唯一标识符
if (string.IsNullOrEmpty(_id)) _id = Guid.NewGuid().ToString();
return _id;
}
set
{
_id = value;
}
} /// <summary>
/// 显示值
/// </summary>
public string Text { get; set; } /// <summary>
/// 绑定值
/// </summary>
public object Value { get; set; } /// <summary>
/// 类型,-1:根;0:原始视频;1:片段
/// </summary>
public VideoTreeControlItemType Type { get; set; } = VideoTreeControlItemType.Original; /// <summary>
/// 子集
/// </summary>
public ObservableCollection<VideoTreeControlItemModel> Children { get; set; } = new ObservableCollection<VideoTreeControlItemModel>(); /// <summary>
/// 文件个数,Type=root时有效
/// </summary>
public int FileCount
{
get
{
if (Type != VideoTreeControlItemType.Root) return ;
List<VideoTreeControlItemModel> outlist = new List<VideoTreeControlItemModel>();
GetFiles(this, outlist);
return outlist.Count;
}
} /// <summary>
/// 获取当前model下所有的文件实体,包含自己
/// </summary>
/// <param name="model">指定的对象</param>
/// <param name="outlist">匹配到的从这里返回</param>
private void GetFiles(VideoTreeControlItemModel model, List<VideoTreeControlItemModel> outlist)
{
if (outlist == null) outlist = new List<VideoTreeControlItemModel>(); if (model.Type != VideoTreeControlItemType.Root)
{
outlist.Add(model);
}
foreach (var item in model.Children)
{
GetFiles(item, outlist);
}
}
} /// <summary>
/// 视频树,子项类型
/// </summary>
public enum VideoTreeControlItemType
{
/// <summary>
/// 根
/// </summary>
Root = -,
/// <summary>
/// 原始视频
/// </summary>
Original = ,
/// <summary>
/// 片段
/// </summary>
Fragment = ,
/// <summary>
/// 序列化
/// </summary>
Sequence =
}
}

上面是完整效果的全部源码,下面我讲介绍我在封装的时候遇到的问题,并附上解决方案

1、如何自定义图标

答:系统内置一些图标在资源里面,通过属性值绑定不同的图片,因为图标总共就3个所有无需调用者指定,直接内置,通过类型区分匹配即可

下面圈注关键代码

2、如何生成通行的分割线

答:通过Canvas来画线,线条尽可能的长,已达到通行的效果,并且还要距左为负数,已抵消数字控件的子集缩进

附上关键代码

3、自定义树的背景效果加上上,发现一个很囧的问题,获得焦点和失去焦点的节点背景很丑,默认获得焦点是蓝色背景,失去焦点是白色背景,欧码噶

答:肯定是要干掉,必须干掉

附上关键代码

4、由于问题3,又新出一个诡异的问题,就是遮挡不完全,会出现一个1-2像素的竖线,经过调试查看发现是节点内置了border导致的,然后这个又没办法重写掉

答:几经周折,发现利用Margin可以抵消,真是大快人心

附上bug图

这是获得焦点

这是失去焦点

解决的关键代码

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界的更多相关文章

  1. C# TreeView 拖拽节点到另一个容器Panel中简单实现

    C# TreeView 拖拽节点到另一个容器Panel中简单实现 用了这么久C#拖拽功能一直没有用到也就没用过,今天因为项目需要,领导特地给我简单讲解了下拖拽功能,真是的大师讲解一点通啊.特地写一篇博 ...

  2. TabTopUnderLineLayout【自定义顶部选项卡(带下划线)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 自定义顶部选项卡布局LinearLayout类,实现带下划线样式的效果. 备注:如果配合Fragment的话,MainActivit ...

  3. winform 两个TreeView间拖拽节点

    /// <summary> /// 正在拖拽的节点 /// </summary> private TreeNode DragNode = null; /// <summa ...

  4. java项目启动后,数据库字段生成 user_name带下划线这种形式的

    hibernate 5.0 版本以上去掉了 hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy  这个属性,如果非 ...

  5. 解决通过Nginx转发的服务请求头header中含有下划线的key,其值取不到的问题

    1. 问题 由于在http请求头的头部中设置了一些自定义字段,刚好这些字段中含有下划线,比如bundle_name这种,后端在进去获取头部信息时,发现取不到对应的值 2. 原因及解决办法 分析 首先看 ...

  6. 解决文字和text-decoration:underline下划线重叠问题

    一.text-decoration:underline下划线的问题 CSS text-decoration:underline可以给内联文本增加下划线,但是,如果对细节要求较高,就会发现,下划线经常会 ...

  7. Delphi Treeview 用法(概念、属性、添加编辑插入节点、定位节点、拖拽等)

    今天再细研究了一下Treeview的用法,网上虽然总结了很多,但是还是有很多节点没有讲到了,也给使用中遇到很多问题.特地总结一下: 1.概念 Treeview用于显示按照树形结构进行组织的数据.Tre ...

  8. 使用knockout-sortable实现对自定义菜单的拖拽排序

    在开始之前,照例,我们先看效果和功能实现. 关于自定义菜单的实现,这里就不多说了,需要了解的请访问:http://www.cnblogs.com/codelove/p/4838766.html 这里需 ...

  9. 【界面优化】使用viewpagerindicator添加下划线滑动动画

    开源代码viewpagerindicator里面没有实现tab下划线切换过程中的移动动画,都是很突兀的多个fragement之间的切换,导致用户体验略差,google了下相关问题,发现一片博文: ht ...

随机推荐

  1. [网站公告]数据库服务器IOPS跑满造成网站不能正常访问

    今年下午13:20-14:20左右,突增的访问量引发数据库服务器(阿里云RDS)IOPS跑满,造成大量请求执行缓慢,从而严重影响了网站的正常访问,给大家带来很大的麻烦,望大家谅解! 在出现故障时,当我 ...

  2. EF-实体更新

    1.数据库表增加字段,EF更新视图后,对应的实体对象没有新增的字段原因:edmx文件右键属性设置了 保存时转换相关的文本模板-false...正确的应该是rue 2. 更改视图后(或者更改字段类型?) ...

  3. AngularJS语法格式小结

    //创建一个最大的容器,"唯一的名字" []数组 var a=angular.module("abcd",[]); //控制器 a.controller(&qu ...

  4. MySQL 5.6.26源码安装

    5.6.26源码安装包:http://pan.baidu.com/s/1kUl44WRcmake安装包链接:http://pan.baidu.com/s/1c0LuwJA 操作系统版本:CentOS ...

  5. vue-修改vue项目运行端口号

    一.导语 最近在研究,左侧是导航,右侧是显示对应的内容,左右可单独滚动,不互相影响,如何实现? 萝卜蹲的游戏大家都玩过,白萝卜蹲,白萝卜蹲,白萝卜蹲完红萝卜蹲,可是若是在含有滚动条的页面的情况下,白萝 ...

  6. Linux(CentOS7.1)修改默认yum源为国内的阿里云yum源

    官方的yum源在国内访问效果不佳. 需要改为国内比较好的阿里云或者网易的yum源 修改方式: 下载wget yum install wget -y echo 备份当前的yum源 mv /etc/yum ...

  7. Eclipse插件安装springBoot

    首先说的是Eclipse的springBoot开发 首先查看你自己的eclipse的版本,点击help-->about Eclipse查看你自己的eclipse的版本 找到 Eclipse的版本 ...

  8. answer my questions from the book&lt;构建之法&gt;.

    1)何为文档:文档时在一个项目进行的一生中所有记忆的集合.有需求分析.功能设计.在实现功能过程中也可以有一系列文档记录.测试文档等等. 2)结对工作等找队友会花费大量时间致耽误项目否:正如老师所讲,从 ...

  9. 阿里云ECS 介绍

    1.阿里云产品概述 1 2.阿里云基础架构介绍 2 3. ECS产品概念和功能 6 4. ECS运维管理和API 12 1.阿里云产品概述 2.阿里云基础架构介绍 ECS 主要有五个主要的组成部分 作 ...

  10. Slickflow.NET 开源工作流引擎高级开发(四) -- 硬核编码:代码式快速构建流程图

    前言:通过设计器交互来创建流程图是比较常见的方式,这种方式是比较方便业务人员对流程的操作.然而,在需要流程模板,或者技术开发阶段以及一些自动化流程的处理过程中,使用代码快速创建流程图也是一种非常有必要 ...