WPF树控件实现节点拖动功能

引:https://blog.csdn.net/run_guo/article/details/100170423

需求:

    作为一名程序员,相信大家都已经接触过大名鼎鼎的原型设计工具Axure,没用过的一定要去了解一下,本文要使用WPF实现树控件节点拖动功能。

  • 实现拖动到目标节点的子目录中

       

  • 实现拖动到目标节点的同级目录中

       

实现:

原理:树节点由图片和文本构成,当拖动节点移动到目标节点的图片上时认为是拖动到目标节点的同级目录;当拖动节点到目标节点的文本上时认为是拖动到目标节点的子目录。使用EventSetter和事件实现标识是拖动到目标节点同级目录,还是拖动到目标节点子目录功能;DataTrigger实现拖动到图片上和文本上的节点显示效果。

前台代码:

 
  1. <Window x:Class="DragTree.MainWindow"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. xmlns:local="clr-namespace:DragTree"

  7. mc:Ignorable="d"

  8. Title="MainWindow" Height="350" Width="525">

  9.  
  10. <Window.Resources>

  11. <HierarchicalDataTemplate x:Key="TreeViewDropItemTemplate" ItemsSource="{Binding Childern}">

  12. <Grid>

  13. <Grid.ColumnDefinitions>

  14. <ColumnDefinition Width="16"/>

  15. <ColumnDefinition/>

  16. </Grid.ColumnDefinitions>

  17. <Grid.RowDefinitions>

  18. <RowDefinition/>

  19. <RowDefinition Height="auto"/>

  20. </Grid.RowDefinitions>

  21. <Image x:Name="img" Grid.Column="0" Margin="2,0" Source="{Binding BI}">

  22. <Image.Style>

  23. <Style>

  24. <EventSetter Event="Image.MouseEnter" Handler="Image_MouseEnter"/>

  25. <EventSetter Event="Image.MouseLeave" Handler="Image_MouseLeave"/>

  26. </Style>

  27. </Image.Style>

  28. </Image>

  29. <TextBlock x:Name="tb" Grid.Column="1" Text="{Binding Name}">

  30. <TextBlock.Style>

  31. <Style>

  32. <EventSetter Event="TextBlock.MouseEnter" Handler="TextBlock_MouseEnter"/>

  33. <EventSetter Event="TextBlock.MouseLeave" Handler="TextBlock_MouseLeave"/>

  34. </Style>

  35. </TextBlock.Style>

  36. </TextBlock>

  37. <Grid x:Name="r1" Grid.Row="1" Grid.ColumnSpan="2" Height="4" Visibility="Collapsed">

  38. <Ellipse Fill="Blue" StrokeThickness="0" Width="4" Height="4" HorizontalAlignment="Left"/>

  39. <Rectangle Fill="Blue" StrokeThickness="0" Height="1" VerticalAlignment="Center"/>

  40. </Grid>

  41. <Grid x:Name="r2" Grid.Row="1" Grid.Column="1" Height="4" Visibility="Collapsed">

  42. <Ellipse Fill="Blue" StrokeThickness="0" Width="4" Height="4" HorizontalAlignment="Left"/>

  43. <Rectangle Fill="Blue" StrokeThickness="0" Height="1" VerticalAlignment="Center"/>

  44. </Grid>

  45. </Grid>

  46. <HierarchicalDataTemplate.Triggers>

  47. <DataTrigger Binding="{Binding Path=IsAdd}" Value="true" >

  48. <Setter TargetName="r1" Property="Visibility" Value="Visible"/>

  49. </DataTrigger>

  50. <DataTrigger Binding="{Binding Path=IsAddIn}" Value="true" >

  51. <Setter TargetName="r2" Property="Visibility" Value="Visible"/>

  52. </DataTrigger>

  53. </HierarchicalDataTemplate.Triggers>

  54. </HierarchicalDataTemplate>

  55. </Window.Resources>

  56. <Grid Margin="50">

  57. <TreeView x:Name="treeView" ItemTemplate="{DynamicResource TreeViewDropItemTemplate}" PreviewMouseDown="tvRequire_PreviewMouseDown"

  58. MouseMove="tvRequire_MouseMove" MouseUp="tvRequire_MouseUp"

  59. MouseLeave="tvRequire_MouseLeave"/>

  60. </Grid>

  61. </Window>

后台代码:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Collections.ObjectModel;

  4. using System.ComponentModel;

  5. using System.Linq;

  6. using System.Windows;

  7. using System.Windows.Controls;

  8. using System.Windows.Input;

  9. using System.Windows.Media.Imaging;

  10.  
  11. namespace DragTree

  12. {

  13. /// <summary>

  14. /// MainWindow.xaml 的交互逻辑

  15. /// </summary>

  16. public partial class MainWindow : Window

  17. {

  18. #region

  19. private bool IsDrop = false;

  20.  
  21. private ObservableCollection<TreeItem> list = new ObservableCollection<TreeItem>();

  22. #endregion

  23.  
  24. public MainWindow()

  25. {

  26. InitializeComponent();

  27. InitTree();

  28. }

  29.  
  30. #region

  31. /// <summary>

  32. /// 树控件数据初始化

  33. /// </summary>

  34. public void InitTree()

  35. {

  36. var folder = GetBitmapImage("folder.png");

  37. var file = GetBitmapImage("file.png");

  38.  
  39. // 根节点

  40. var root = new TreeItem()

  41. {

  42. Name = "root",

  43. Childern = list

  44. };

  45.  
  46. var ti = new TreeItem()

  47. {

  48. BI = folder,

  49. Name = "文件夹1",

  50. Parent = root,

  51. Childern = new ObservableCollection<TreeItem>()

  52. };

  53.  
  54. var tmp11 = new TreeItem()

  55. {

  56. BI = folder,

  57. Name = "文件夹11",

  58. Parent = ti,

  59. Childern = new ObservableCollection<TreeItem>()

  60. };

  61.  
  62. var tmp111 = new TreeItem()

  63. {

  64. BI = file,

  65. Name = "文件111",

  66. Parent = tmp11,

  67. Childern = new ObservableCollection<TreeItem>()

  68. };

  69. tmp11.Childern.Add(tmp111);

  70. var tmp112 = new TreeItem()

  71. {

  72. BI = file,

  73. Name = "文件112",

  74. Parent = tmp11,

  75. Childern = new ObservableCollection<TreeItem>()

  76. };

  77. tmp11.Childern.Add(tmp112);

  78. var tmp113 = new TreeItem()

  79. {

  80. BI = file,

  81. Name = "文件113",

  82. Parent = tmp11,

  83. Childern = new ObservableCollection<TreeItem>()

  84. };

  85. tmp11.Childern.Add(tmp113);

  86.  
  87. ti.Childern.Add(tmp11);

  88.  
  89. var tmp12 = new TreeItem()

  90. {

  91. BI = folder,

  92. Name = "文件夹12",

  93. Parent = ti,

  94. Childern = new ObservableCollection<TreeItem>()

  95. };

  96. ti.Childern.Add(tmp12);

  97.  
  98. var tmp13 = new TreeItem()

  99. {

  100. BI = folder,

  101. Name = "文件夹13",

  102. Parent = ti,

  103. Childern = new ObservableCollection<TreeItem>()

  104. };

  105. ti.Childern.Add(tmp13);

  106.  
  107. list.Add(ti);

  108.  
  109. this.treeView.ItemsSource = list;

  110. }

  111.  
  112. private Point _lastMouseDown;

  113. private void tvRequire_PreviewMouseDown(object sender, MouseButtonEventArgs e)

  114. {

  115. if (e.ChangedButton == MouseButton.Left)

  116. {

  117. var ele = e.OriginalSource as FrameworkElement;

  118. if (ele != null)

  119. {

  120. _lastMouseDown = e.GetPosition(treeView);

  121. moveTreeItem = (TreeItem)ele.DataContext;

  122. }

  123. }

  124. }

  125.  
  126. private TreeItem moveTreeItem;

  127. private void tvRequire_MouseMove(object sender, MouseEventArgs e)

  128. {

  129. if (e.LeftButton == MouseButtonState.Pressed)

  130. {

  131. Point currentPosition = e.GetPosition(treeView);

  132. if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 2.0) || (Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 2.0))

  133. {

  134. if (!IsDrop)

  135. {

  136. if (moveTreeItem != null)

  137. {

  138. IsDrop = true;

  139. this.treeView.Cursor = Cursors.Hand;

  140. }

  141. }

  142. }

  143. }

  144. }

  145.  
  146. private void tvRequire_MouseUp(object sender, MouseEventArgs e)

  147. {

  148. if (moveTreeItem == null)

  149. {

  150. return;

  151. }

  152. var ele = e.OriginalSource as FrameworkElement;

  153. if (ele != null)

  154. {

  155. var targetTreeItem = (TreeItem)ele.DataContext;

  156. if (targetTreeItem == null)

  157. {

  158. ClearTreeDrop();

  159. return;

  160. }

  161. if (targetTreeItem.Name != moveTreeItem.Name)

  162. {

  163. if (targetTreeItem.IsAddIn)

  164. {

  165. #region 移动到目标子集

  166. var tmp = targetTreeItem.Childern.SingleOrDefault(l => l.Name == moveTreeItem.Name);

  167. if (tmp == null)

  168. {

  169. moveTreeItem.Parent.Childern.Remove(moveTreeItem);

  170. moveTreeItem.Parent = targetTreeItem;

  171. AddIn(targetTreeItem, moveTreeItem);

  172. }

  173. targetTreeItem.IsAddIn = false;

  174. #endregion

  175. }

  176. else if (targetTreeItem.IsAdd)

  177. {

  178. #region 移动到目标同级

  179. moveTreeItem.Parent.Childern.Remove(moveTreeItem);

  180. Add(targetTreeItem.Parent, targetTreeItem, moveTreeItem);

  181. targetTreeItem.IsAdd = false;

  182. #endregion

  183. }

  184. }

  185. }

  186. ClearTreeDrop();

  187. }

  188.  
  189. private void AddIn(TreeItem argTarget, TreeItem argMove)

  190. {

  191. if (argTarget != null)

  192. {

  193. var sortList = new List<TreeItem>();

  194. sortList.Add(argMove);

  195. foreach (var item in argTarget.Childern)

  196. {

  197. sortList.Add(item);

  198. }

  199. argTarget.Childern.Clear();

  200. foreach (var item in sortList)

  201. {

  202. argTarget.Childern.Add(item);

  203. }

  204. }

  205. }

  206.  
  207. private void Add(TreeItem argParent, TreeItem argTarget, TreeItem argMove)

  208. {

  209. if (argParent != null)

  210. {

  211. var sortList = new List<TreeItem>();

  212. foreach (var item in argParent.Childern)

  213. {

  214. sortList.Add(item);

  215.  
  216. if (item.Name == argTarget.Name)

  217. {

  218. argMove.Parent = item.Parent;

  219. sortList.Add(argMove);

  220. }

  221. }

  222. argParent.Childern.Clear();

  223. foreach (var item in sortList)

  224. {

  225. argParent.Childern.Add(item);

  226. }

  227. }

  228. }

  229.  
  230. private void tvRequire_MouseLeave(object sender, MouseEventArgs e)

  231. {

  232. ClearTreeDrop();

  233. }

  234.  
  235. private void ClearTreeDrop()

  236. {

  237. moveTreeItem = null;

  238. IsDrop = false;

  239. this.treeView.Cursor = Cursors.Arrow;

  240. }

  241.  
  242. public BitmapImage GetBitmapImage(string argName, string argCatalog = null)

  243. {

  244. try

  245. {

  246. string path = string.Empty;

  247. if (string.IsNullOrEmpty(argCatalog))

  248. {

  249. path = @"pack://application:,,,/Image/" + argName;

  250. }

  251. else

  252. {

  253. path = @"pack://application:,,,/Image/" + argCatalog + "/" + argName;

  254. }

  255. var bi = new BitmapImage();

  256. bi.BeginInit();

  257. // 创建 BitmapImage 后关闭流,请将 CacheOption 属性设置为 BitmapCacheOption.OnLoad。

  258. // 默认 OnDemand 缓存选项保留对流的访问,直至需要位图并且垃圾回收器执行清理为止。

  259. bi.CacheOption = BitmapCacheOption.OnLoad;

  260. bi.UriSource = new Uri(path);

  261. bi.EndInit();

  262. bi.Freeze();

  263. return bi;

  264. }

  265. catch (Exception ex)

  266. {

  267. return null;

  268. }

  269. }

  270.  
  271. private void Image_MouseEnter(object sender, MouseEventArgs e)

  272. {

  273. var ele = e.OriginalSource as Image;

  274. if (ele != null)

  275. {

  276. Console.WriteLine("Image_MouseEnter");

  277. var ti = (TreeItem)ele.DataContext;

  278. if (ti != null)

  279. {

  280. if (IsDrop)

  281. {

  282. ti.IsAdd = true;

  283. }

  284. }

  285. }

  286. }

  287.  
  288. private void Image_MouseLeave(object sender, MouseEventArgs e)

  289. {

  290. var ele = e.OriginalSource as Image;

  291. if (ele != null)

  292. {

  293. Console.WriteLine("Image_MouseLeave");

  294. var ti = ele.DataContext as TreeItem;

  295. if (ti != null)

  296. {

  297. ti.IsAdd = false;

  298. }

  299. }

  300. }

  301.  
  302. private void TextBlock_MouseEnter(object sender, MouseEventArgs e)

  303. {

  304. var ele = e.OriginalSource as TextBlock;

  305. if (ele != null)

  306. {

  307. Console.WriteLine("TextBlock_MouseEnter");

  308. var ti = (TreeItem)ele.DataContext;

  309. if (ti != null)

  310. {

  311. if (IsDrop)

  312. {

  313. ti.IsAddIn = true;

  314. }

  315. }

  316. }

  317. }

  318.  
  319. private void TextBlock_MouseLeave(object sender, MouseEventArgs e)

  320. {

  321. var ele = e.OriginalSource as TextBlock;

  322. if (ele != null)

  323. {

  324. Console.WriteLine("TextBlock_MouseLeave");

  325. var ti = (TreeItem)ele.DataContext;

  326. if (ti != null)

  327. {

  328. ti.IsAddIn = false;

  329. }

  330. }

  331. }

  332. #endregion

  333.  
  334. #region

  335. public class TreeItem : INotifyPropertyChanged

  336. {

  337. public event PropertyChangedEventHandler PropertyChanged;

  338.  
  339. /// <summary>

  340. /// 节点显示的图片

  341. /// </summary>

  342. public BitmapImage BI { get; set; }

  343.  
  344. /// <summary>

  345. /// 节点显示的文本

  346. /// </summary>

  347. public string Name { get; set; }

  348.  
  349. /// <summary>

  350. /// 节点类型

  351. /// </summary>

  352. public string Type { get; set; }

  353.  
  354. /// <summary>

  355. /// 父节点

  356. /// </summary>

  357. public TreeItem Parent { get; set; }

  358.  
  359. /// <summary>

  360. /// 子节点集合

  361. /// </summary>

  362. public ObservableCollection<TreeItem> Childern { get; set; }

  363.  
  364. private bool m_isAdd = false;

  365. /// <summary>

  366. /// 拖动到目标节点时增加到同级目录标志

  367. /// </summary>

  368. public bool IsAdd

  369. {

  370. get { return m_isAdd; }

  371. set

  372. {

  373. if (m_isAdd != value)

  374. {

  375. m_isAdd = value;

  376. if (m_isAdd)

  377. {

  378. IsAddIn = false;

  379. }

  380. if (PropertyChanged != null)

  381. {

  382. PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsAdd"));

  383. }

  384. }

  385. }

  386. }

  387.  
  388. private bool m_isAddin = false;

  389. /// <summary>

  390. /// 拖动到目标节点时增加到子目录标志

  391. /// </summary>

  392. public bool IsAddIn

  393. {

  394. get { return m_isAddin; }

  395. set

  396. {

  397. if (m_isAddin != value)

  398. {

  399. m_isAddin = value;

  400. if (m_isAddin)

  401. {

  402. m_isAdd = false;

  403. }

  404. //触发事件

  405. if (PropertyChanged != null)

  406. {

  407. PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsAddIn"));

  408. }

  409. }

  410. }

  411. }

  412. }

  413. #endregion

  414. }

  415. }

效果:

  

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值