欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-01 11:24:34
1、运行效果: 2、前端代码 1

1、运行效果:

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

2、前端代码

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界
 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、控件的后台代码

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界
  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     /// <summary>
 21     /// VideoTreeControl.xaml 的交互逻辑
 22     /// </summary>
 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         /// <summary>
 38         /// 上下文
 39         /// </summary>
 40         VideoTreeControlViewModel vm
 41         {
 42             get
 43             {
 44                 return this.DataContext as VideoTreeControlViewModel;
 45             }
 46         }
 47 
 48         /// <summary>
 49         /// 背景颜色
 50         /// </summary>
 51         public string VideoTreeControlBackground
 52         {
 53             get { return (string)GetValue(VideoTreeControlBackgroundProperty); }
 54             set { SetValue(VideoTreeControlBackgroundProperty, value); }
 55         }
 56 
 57         /// <summary>
 58         /// 字体颜色
 59         /// </summary>
 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         /// <summary>
 77         /// 双击节点
 78         /// </summary>
 79         public event EventHandler<VideoTreeControlItemModel> DoubleClickTreeItem;
 80 
 81         /// <summary>
 82         /// 节点选中变更
 83         /// </summary>
 84         public event EventHandler<VideoTreeControlItemModel> SelectedItemChanged;
 85         #endregion
 86 
 87         /// <summary>
 88         /// 选中时
 89         /// </summary>
 90         /// <param name="sender"></param>
 91         /// <param name="e"></param>
 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         /// <summary>
111         /// 双击节点
112         /// </summary>
113         /// <param name="sender"></param>
114         /// <param name="e"></param>
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对象

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界
 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<VideoTreeControlItemModel> _DataList;
18 
19         /// <summary>
20         /// 数据源
21         /// </summary>
22         public ObservableCollection<VideoTreeControlItemModel> 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<VideoTreeControlItemModel>() {
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、数据实体 

重写TreeView,自定义图标,生成通行的下划线,取消默认获得焦点失去焦点的效果,并支持拖拽节点到外界
  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     /// <summary>
 11     /// 视频图片树状控件,数据映射实体
 12     /// </summary>
 13     public class VideoTreeControlItemModel
 14     {
 15         private string _id = string.Empty;
 16         /// <summary>
 17         /// 唯一标示符
 18         /// </summary>
 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         /// <summary>
 34         /// 显示值
 35         /// </summary>
 36         public string Text { get; set; }
 37 
 38         /// <summary>
 39         /// 绑定值
 40         /// </summary>
 41         public object Value { get; set; }
 42 
 43         /// <summary>
 44         /// 类型,-1:根;0:原始视频;1:片段
 45         /// </summary>
 46         public VideoTreeControlItemType Type { get; set; } = VideoTreeControlItemType.Original;
 47 
 48         /// <summary>
 49         /// 子集
 50         /// </summary>
 51         public ObservableCollection<VideoTreeControlItemModel> Children { get; set; } = new ObservableCollection<VideoTreeControlItemModel>();
 52 
 53         /// <summary>
 54         /// 文件个数,Type=root时有效
 55         /// </summary>
 56         public int FileCount
 57         {
 58             get
 59             {
 60                 if (Type != VideoTreeControlItemType.Root) return 0;
 61                 List<VideoTreeControlItemModel> outlist = new List<VideoTreeControlItemModel>();
 62                 GetFiles(this, outlist);
 63                 return outlist.Count;
 64             }
 65         }
 66 
 67         /// <summary>
 68         /// 获取当前model下所有的文件实体,包含自己
 69         /// </summary>
 70         /// <param name="model">指定的对象</param>
 71         /// <param name="outlist">匹配到的从这里返回</param>
 72         private void GetFiles(VideoTreeControlItemModel model, List<VideoTreeControlItemModel> outlist)
 73         {
 74             if (outlist == null) outlist = new List<VideoTreeControlItemModel>();
 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     /// <summary>
 88     /// 视频树,子项类型
 89     /// </summary>
 90     public enum VideoTreeControlItemType
 91     {
 92         /// <summary>
 93         /// 根
 94         /// </summary>
 95         Root = -1,
 96         /// <summary>
 97         /// 原始视频
 98         /// </summary>
 99         Original = 0,
100         /// <summary>
101         /// 片段
102         /// </summary>
103         Fragment = 1,
104         /// <summary>
105         /// 序列化
106         /// </summary>
107         Sequence = 2
108     }
109 }
View Code

 


 

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

1、如何自定义图标

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

下面圈注关键代码

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

 

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

 

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

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

附上关键代码

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

 

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

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

附上关键代码

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

 

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

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

附上bug图

这是获得焦点

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

 

这是失去焦点

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

 

解决的关键代码

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