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

1、运行效果:

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第1张图片重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第2张图片

2、前端代码

 1 <UserControl x:Class="iPIS.UI.Base.Tree.VideoTreeControl"
 2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 5              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 6              xmlns:local="clr-namespace:iPIS.UI.Base.Tree"
 7              mc:Ignorable="d" 
 8              d:DesignHeight="50" d:DesignWidth="80">
 9     <UserControl.Resources>
10         <ResourceDictionary>
11             <ResourceDictionary.MergedDictionaries>
12                 <ResourceDictionary Source="/iPIS.UI.Themes.Black;component/Base/Tree/VideoTreeControlImages.xaml"/>
13             ResourceDictionary.MergedDictionaries>
14             <Style TargetType="TextBlock" x:Key="fontstyle">
15                 <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlFontColor}">Setter>
16                 <Setter Property="FontSize" Value="14">Setter>
17                 <Setter Property="VerticalAlignment" Value="Center">Setter>
18             Style>
19         ResourceDictionary>
20     UserControl.Resources>
21     <Grid>
22         <TreeView x:Name="tree" 
23               AllowDrop="True" 
24               ItemsSource="{Binding DataList}"                  
25               Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlBackground}"
26               >
27             <TreeView.ItemTemplate>
28                 <HierarchicalDataTemplate ItemsSource="{Binding Children}">
29                     <Grid x:Name="grid"
30                           Margin="-1 0 0 0" 
31                           Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlBackground}"
32                           >
33                         <StackPanel Grid.Column="1" 
34                                     Orientation="Horizontal" 
35                                     Cursor="Hand" 
36                                     Height="40"
37                                     VerticalAlignment="Center"
38                                 >
39                             <Image x:Name="icon" Width="19" Height="16" VerticalAlignment="Center" Margin="0 0 5 0">Image>
40                             <TextBlock Text="{Binding Text}" Style="{StaticResource fontstyle}">TextBlock>
41                             <StackPanel x:Name="cc" 
42                                         Orientation="Horizontal" 
43                                         Visibility="Collapsed"
44                                         Margin="0 0 -1 0"
45                                         Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:VideoTreeControl},Path=VideoTreeControlBackground}"
46                                         >
47                                 <TextBlock Style="{StaticResource fontstyle}">(TextBlock>
48                                 <TextBlock Text="{Binding FileCount}" Style="{StaticResource fontstyle}">TextBlock>
49                                 <TextBlock Style="{StaticResource fontstyle}">)TextBlock>
50                             StackPanel>
51                         StackPanel>
52                         
53                         <Canvas>
54                             <Line X1="0" Y1="39" X2="1000" Y2="39" Stroke="#4a4a4a" Margin="-500 0 0 0" StrokeThickness="1">Line>
55                         Canvas>
56                     Grid>
57                     <HierarchicalDataTemplate.Triggers>
58                         <DataTrigger Binding="{Binding Type}" Value="-1">
59                             <Setter TargetName="icon" Property="Source" Value="{StaticResource icon_default}">Setter>
60                         DataTrigger>
61                         <DataTrigger Binding="{Binding Type}" Value="0">
62                             <Setter TargetName="icon" Property="Source" Value="{StaticResource icon_yuan}">Setter>
63                         DataTrigger>
64                         <DataTrigger Binding="{Binding Type}" Value="1">
65                             <Setter TargetName="icon" Property="Source" Value="{StaticResource icon_pian}">Setter>
66                         DataTrigger>
67                         <DataTrigger Binding="{Binding Type}" Value="2">
68                             <Setter TargetName="icon" Property="Source" Value="{StaticResource icon_xulie}">Setter>
69                         DataTrigger>
70                         <DataTrigger Binding="{Binding Type}" Value="-1">
71                             <Setter TargetName="cc" Property="Visibility" Value="Visible">Setter>
72                         DataTrigger>
73                     HierarchicalDataTemplate.Triggers>
74                 HierarchicalDataTemplate>
75             TreeView.ItemTemplate>
76         TreeView>
77     Grid>
78 UserControl>
View Code

3、控件的后台代码

  1 using iPIS.UI.Base.Model;
  2 using iPIS.UI.Base.ViewModel;
  3 using System;
  4 using System.Collections.Generic;
  5 using System.Linq;
  6 using System.Text;
  7 using System.Threading.Tasks;
  8 using System.Windows;
  9 using System.Windows.Controls;
 10 using System.Windows.Data;
 11 using System.Windows.Documents;
 12 using System.Windows.Input;
 13 using System.Windows.Media;
 14 using System.Windows.Media.Imaging;
 15 using System.Windows.Navigation;
 16 using System.Windows.Shapes;
 17 
 18 namespace iPIS.UI.Base.Tree
 19 {
 20     /// 
 21     /// VideoTreeControl.xaml 的交互逻辑
 22     /// 
 23     public partial class VideoTreeControl : UserControl
 24     {
 25         public VideoTreeControl()
 26         {
 27             InitializeComponent();
 28             this.DataContext = new VideoTreeControlViewModel();
 29             //默认样式
 30             VideoTreeControlBackground = "#36353a";
 31             VideoTreeControlFontColor = "#9a9a9a";
 32 
 33             tree.SelectedItemChanged += Tree_SelectedItemChanged;
 34             tree.MouseDoubleClick += Tree_MouseDoubleClick;
 35         }
 36 
 37         /// 
 38         /// 上下文
 39         /// 
 40         VideoTreeControlViewModel vm
 41         {
 42             get
 43             {
 44                 return this.DataContext as VideoTreeControlViewModel;
 45             }
 46         }
 47 
 48         /// 
 49         /// 背景颜色
 50         /// 
 51         public string VideoTreeControlBackground
 52         {
 53             get { return (string)GetValue(VideoTreeControlBackgroundProperty); }
 54             set { SetValue(VideoTreeControlBackgroundProperty, value); }
 55         }
 56 
 57         /// 
 58         /// 字体颜色
 59         /// 
 60         public string VideoTreeControlFontColor
 61         {
 62             get { return (string)GetValue(VideoTreeControlFontColorProperty); }
 63             set { SetValue(VideoTreeControlFontColorProperty, value); }
 64         }
 65 
 66         #region 附加属性
 67 
 68         public static readonly DependencyProperty VideoTreeControlFontColorProperty =
 69             DependencyProperty.Register("VideoTreeControlFontColor", typeof(string), typeof(VideoTreeControl), new PropertyMetadata(""));
 70 
 71         public static readonly DependencyProperty VideoTreeControlBackgroundProperty =
 72             DependencyProperty.Register("VideoTreeControlBackground", typeof(string), typeof(VideoTreeControl), new PropertyMetadata(""));
 73         #endregion
 74 
 75         #region 公开事件
 76         /// 
 77         /// 双击节点
 78         /// 
 79         public event EventHandler DoubleClickTreeItem;
 80 
 81         /// 
 82         /// 节点选中变更
 83         /// 
 84         public event EventHandler SelectedItemChanged;
 85         #endregion
 86 
 87         /// 
 88         /// 选中时
 89         /// 
 90         /// 
 91         /// 
 92         private void Tree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
 93         {
 94             var datamodel = e.NewValue as VideoTreeControlItemModel;
 95             if (datamodel.Type == VideoTreeControlItemType.Root) return;//过滤root
 96             SelectedItemChanged?.Invoke(vm, datamodel);
 97             //获取方式   e.Data.GetData(typeof(Base.Model.VideoTreeControlItemModel))
 98             DragDrop.DoDragDrop(sender as FrameworkElement, datamodel, DragDropEffects.Copy);
 99 
100             /*
101             DragDrop.DoDragDrop(sender as FrameworkElement, new ImageTreeControlImageModel()
102             {
103                 Text = "我是拖进来的",
104                 Type = ImageTreeControlImageType.AlreadyMatting,
105                 ImageSource = new BitmapImage(new Uri("D:\\上传资源\\xxx.png", UriKind.Absolute))
106             }, DragDropEffects.Copy);
107             */
108         }
109 
110         /// 
111         /// 双击节点
112         /// 
113         /// 
114         /// 
115         private void Tree_MouseDoubleClick(object sender, MouseButtonEventArgs e)
116         {
117             var datamodel = tree.SelectedValue as VideoTreeControlItemModel;
118             if (datamodel == null || datamodel.Type == VideoTreeControlItemType.Root) return;//过滤root
119             DoubleClickTreeItem?.Invoke(vm, datamodel);
120         }
121     }
122 }
View Code

4、控件的datacontext对象

 1 using iPIS.UI.Base.Model;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Collections.ObjectModel;
 5 using System.ComponentModel;
 6 using System.Linq;
 7 using System.Text;
 8 using System.Threading.Tasks;
 9 using System.Windows.Input;
10 
11 namespace iPIS.UI.Base.ViewModel
12 {
13     public class VideoTreeControlViewModel : INotifyPropertyChanged
14     {
15         public event PropertyChangedEventHandler PropertyChanged;
16 
17         private ObservableCollection _DataList;
18 
19         /// 
20         /// 数据源
21         /// 
22         public ObservableCollection DataList
23         {
24             get => _DataList;
25             set
26             {
27                 //检查是否有roo,不存在生成默认root
28                 if (value.Where(c => c.Type == VideoTreeControlItemType.Root).Count() == 0)
29                 {
30                     _DataList = new ObservableCollection() {
31                         new VideoTreeControlItemModel(){
32                             Text = "默认",
33                             Children = value,
34                             Type = VideoTreeControlItemType.Root//标示为root
35                         }
36                     };
37                 }
38                 else
39                     _DataList = value;
40                 PropertyChanged?.Notify(() => this.DataList);
41             }
42         }
43     }
44 }
View Code

5、数据实体 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Collections.ObjectModel;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Threading.Tasks;
  7 
  8 namespace iPIS.UI.Base.Model
  9 {
 10     /// 
 11     /// 视频图片树状控件,数据映射实体
 12     /// 
 13     public class VideoTreeControlItemModel
 14     {
 15         private string _id = string.Empty;
 16         /// 
 17         /// 唯一标示符
 18         /// 
 19         public string Id
 20         {
 21             get
 22             {
 23                 //如果调用者未显示给入唯一标识符,将默认生成一个唯一标识符
 24                 if (string.IsNullOrEmpty(_id)) _id = Guid.NewGuid().ToString();
 25                 return _id;
 26             }
 27             set
 28             {
 29                 _id = value;
 30             }
 31         }
 32 
 33         /// 
 34         /// 显示值
 35         /// 
 36         public string Text { get; set; }
 37 
 38         /// 
 39         /// 绑定值
 40         /// 
 41         public object Value { get; set; }
 42 
 43         /// 
 44         /// 类型,-1:根;0:原始视频;1:片段
 45         /// 
 46         public VideoTreeControlItemType Type { get; set; } = VideoTreeControlItemType.Original;
 47 
 48         /// 
 49         /// 子集
 50         /// 
 51         public ObservableCollection Children { get; set; } = new ObservableCollection();
 52 
 53         /// 
 54         /// 文件个数,Type=root时有效
 55         /// 
 56         public int FileCount
 57         {
 58             get
 59             {
 60                 if (Type != VideoTreeControlItemType.Root) return 0;
 61                 List outlist = new List();
 62                 GetFiles(this, outlist);
 63                 return outlist.Count;
 64             }
 65         }
 66 
 67         /// 
 68         /// 获取当前model下所有的文件实体,包含自己
 69         /// 
 70         /// 指定的对象
 71         /// 匹配到的从这里返回
 72         private void GetFiles(VideoTreeControlItemModel model, List outlist)
 73         {
 74             if (outlist == null) outlist = new List();
 75 
 76             if (model.Type != VideoTreeControlItemType.Root)
 77             {
 78                 outlist.Add(model);
 79             }
 80             foreach (var item in model.Children)
 81             {
 82                 GetFiles(item, outlist);
 83             }
 84         }
 85     }
 86 
 87     /// 
 88     /// 视频树,子项类型
 89     /// 
 90     public enum VideoTreeControlItemType
 91     {
 92         /// 
 93         /// 94         /// 
 95         Root = -1,
 96         /// 
 97         /// 原始视频
 98         /// 
 99         Original = 0,
100         /// 
101         /// 片段
102         /// 
103         Fragment = 1,
104         /// 
105         /// 序列化
106         /// 
107         Sequence = 2
108     }
109 }
View Code

 


 

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

1、如何自定义图标

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

下面圈注关键代码

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第3张图片

 

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第4张图片

 

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

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

附上关键代码

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第5张图片

 

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

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

附上关键代码

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第6张图片

 

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

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

附上bug图

这是获得焦点

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第7张图片

 

这是失去焦点

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第8张图片

 

解决的关键代码

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界_第9张图片

你可能感兴趣的:(重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界)