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. option_match

    //与match区别:如果没有匹配到,返回NULLCREATE (olive:Person {name:'Olive Stone'}),(charlie:Person {name:'Charlie S ...

  2. 堆和索引堆的实现(python)

    ''' 索引堆 ''' ''' 实现使用2个辅助数组来做.有点像dat.用哈希表来做修改不行,只是能找到这个索引,而需要change操作 还是需要自己手动写.所以只能用双数组实现. #引入索引堆的核心 ...

  3. 安装配置nfs

    #yum -y install nfs-utils rpcbind #service rpcbind start#service nfs start #chkconfig --add rpcbind# ...

  4. CSS实现背景透明而背景上的文字不透明

    在我们设计制作一些网页的时候可能会用到半透明的效果,首先我们可能会想到用PNG图片处理,当然这是一个不错的办法,唯一的兼容性问题就是ie6 下的BUG,但这也不困难,加上一段js处理就行了.但假如我们 ...

  5. 如何用TFS build definition添加自动建立nuget package 步骤

    参考网址:https://www.visualstudio.com/en-us/docs/build/steps/package/nuget-packager https://docs.nuget.o ...

  6. 2019.02.09 bzoj2440: [中山市选2011]完全平方数(二分答案+容斥原理)

    传送门 题意简述:qqq次询问(q≤500)(q\le500)(q≤500),每次问第kkk个不被除111以外的完全平方数整除的数是多少(k≤1e9)(k\le1e9)(k≤1e9). 思路:考虑二分 ...

  7. springboot Thymeleaf 整合

    Thymeleaf是一个Java模板引擎开发库,可以处理和生成HTML.XML.JavaScript.CSS和文本,在Web和非Web环境下都可以正常工作. Thymeleaf可以跟Spring bo ...

  8. best-case analysis in real-time system

    ECRTS: Exact Best-Case Response Time Analysis of Fixed Priority Scheduled Tasks motivation Real-time ...

  9. Office2013 如何安装Matlab notebook

    Office2013 如何安装Matlab notebook 听语音 浏览:912 | 更新:2014-09-16 07:02 1 2 3 4 5 6 7 分步阅读 Office2013(64bit) ...

  10. mysql学习之路_联合查询与子查询

    联合查询 联合查询:将多次查询(多条select语句)在记录上进行拼接(字段不会增加). 语法:多条select语句构成,每条select语句获取的字段必须严格一致(但是字段类型无关). Select ...