当前位置:   article > 正文

WPF拖放控件

WPF拖放控件

拖放官方文档

拖放操作通常涉及两个参与方:拖动对象所源自的拖动源和接收放置对象的拖放目标。 拖动源和放置目标可能是相同应用程序或不同应用程序中的 UI 元素。

我这里实现的是对TabControl的Tab页面进行拖放,以达成类似Chrome浏览器的拖放功能。

对拖放相关事件研究后发现,最终只需要在拖动源事件处理程序中调用 DoDragDrop 方法启动拖动操作,然后在拖放目标上,响应Drop事件。启动拖放操作的拖动源事件通常是 MouseMove事件,另外需要判断鼠标左键是否为按下状态。另外,最好判断一下鼠标移动的距离是否大于最小拖动距离(在我没有添加这个检查之前,双击也会触发拖动事件)。

除了正常拖动(将Tab页从一个TabControl 拖动到另一个 TabControl 中),考虑将Tab页拖动到窗口之外的情况(浏览器在这种情况下会创建一个新的窗口来显示这个Tab页),我这里采用类似的方式,创建一个预设的子窗口来显示该Tab页面。

此外,当Tab页面在原TabControl内部拖动时,根据拖动位置,调整Tab页面的顺序。

我这里对原生的TabControl进行了继承封装了几个响应事件,轻量化的实现了拖放效果。

TabControlDragable.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Input;
  9. using System.Windows.Media;
  10. namespace WpfLibrary;
  11. public class TabControlDragable : TabControl
  12. {
  13. public TabControlDragable()
  14. {
  15. AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), true);
  16. AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp), true);
  17. AddHandler(MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);
  18. AddHandler(DropEvent, new DragEventHandler(OnDrop), true);
  19. MinWidth = 100;
  20. MinHeight = 100;
  21. AllowDrop = true;
  22. }
  23. private bool IsDraging = false;
  24. private object? DragData;
  25. private Point DragStartPosition;
  26. private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  27. {
  28. Point p = e.GetPosition(this);
  29. DragData = DragUtility.GetDataObjectFromItemsControl(this, p);
  30. if (DragData != null)
  31. {
  32. DragStartPosition = p;
  33. }
  34. e.Handled = true;
  35. }
  36. private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  37. {
  38. ResetState();
  39. e.Handled = true;
  40. }
  41. private void OnMouseMove(object sender, MouseEventArgs e)
  42. {
  43. if (DragData == null || DragData is not TabItem)
  44. {
  45. return;
  46. }
  47. Point currentPosition = e.GetPosition(this);
  48. if (e.LeftButton == MouseButtonState.Pressed && !IsDraging
  49. && ((Math.Abs(currentPosition.X - DragStartPosition.X) > SystemParameters.MinimumHorizontalDragDistance) || (Math.Abs(currentPosition.Y - DragStartPosition.Y) > SystemParameters.MinimumVerticalDragDistance)))
  50. {
  51. DataObject dataObject = new DataObject();
  52. dataObject.SetData("DragData", DragData);
  53. var effects = DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Move);
  54. if (effects == DragDropEffects.None)
  55. {
  56. if (Items.Count != 1)
  57. {
  58. var tabItem = DragData as TabItem;
  59. var parentWindow = DragUtility.GetParentWindow(tabItem);
  60. Items.Remove(tabItem);
  61. ChildWindow newChildWindow = new ChildWindow();
  62. if (parentWindow is ChildWindow)
  63. {
  64. newChildWindow.Owner = parentWindow.Owner;
  65. }
  66. else
  67. {
  68. newChildWindow.Owner = parentWindow;
  69. }
  70. newChildWindow.DataContext = parentWindow?.DataContext;
  71. var tabControl = new TabControlDragable();
  72. newChildWindow.MyGrid.Children.Add(tabControl);
  73. tabControl.Items.Add(tabItem);
  74. newChildWindow.Show();
  75. }
  76. }
  77. ResetState();
  78. }
  79. e.Handled = true;
  80. }
  81. private int MeasureIndex(TabControl targetTabControl, Point point)
  82. {
  83. double preWidth = 0.0;
  84. for (var i = 0; i < targetTabControl.Items.Count; i++)
  85. {
  86. var item = targetTabControl.Items[i] as TabItem;
  87. if (item == null)
  88. {
  89. continue;
  90. }
  91. if (point.Y > item.ActualHeight)
  92. {
  93. return targetTabControl.Items.Count;
  94. }
  95. if (point.X < preWidth + item.ActualWidth / 2)
  96. {
  97. return i;
  98. }
  99. else
  100. {
  101. preWidth += item.ActualWidth;
  102. }
  103. }
  104. return targetTabControl.Items.Count;
  105. }
  106. private void OnDrop(object sender, DragEventArgs e)
  107. {
  108. var tabItem = e.Data.GetData("DragData") as TabItem;
  109. var parentTabControl = tabItem?.Parent as TabControl;
  110. if (tabItem == null || parentTabControl == null)
  111. {
  112. return;
  113. }
  114. var point = e.GetPosition(this);
  115. int index = MeasureIndex(this, point);
  116. if (this != parentTabControl)
  117. {
  118. var parentWindow = DragUtility.GetParentWindow(parentTabControl);
  119. if (parentTabControl.Items.Count == 1)
  120. {
  121. var childWindow = parentWindow as ChildWindow;
  122. if (childWindow != null)
  123. {
  124. parentTabControl.Items.Remove(tabItem);
  125. this.Items.Insert(index, tabItem);
  126. this.SelectedItem = tabItem;
  127. childWindow?.Close();
  128. }
  129. }
  130. else
  131. {
  132. parentTabControl.Items.Remove(tabItem);
  133. this.Items.Insert(index, tabItem);
  134. this.SelectedItem = tabItem;
  135. }
  136. }
  137. else if (index < parentTabControl.Items.IndexOf(tabItem))
  138. {
  139. parentTabControl.Items.Remove(tabItem);
  140. parentTabControl.Items.Insert(index, tabItem);
  141. parentTabControl.SelectedItem = tabItem;
  142. }
  143. else if (index > parentTabControl.Items.IndexOf(tabItem))
  144. {
  145. parentTabControl.Items.Remove(tabItem);
  146. parentTabControl.Items.Insert(index - 1, tabItem);
  147. parentTabControl.SelectedItem = tabItem;
  148. }
  149. e.Handled = true;
  150. }
  151. private void ResetState()
  152. {
  153. IsDraging = false;
  154. DragData = null;
  155. }
  156. }

使用起来也很简单,只需要将TabControl 替换为 TabControlDragable 即可,事件已经绑定好了。

MainWindow.xaml

  1. <Window x:Class="WpfLibraryTest.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:WpfLibraryTest"
  7. xmlns:wpfLibrary="clr-namespace:WpfLibrary;assembly=WpfLibrary"
  8. mc:Ignorable="d"
  9. Title="MainWindow" Height="450" Width="800">
  10. <Grid>
  11. <Grid.RowDefinitions>
  12. <RowDefinition Height="*"/>
  13. <RowDefinition Height="*"/>
  14. </Grid.RowDefinitions>
  15. <wpfLibrary:TabControlDragable Grid.Row="0" x:Name="tabControl1">
  16. <TabItem Header="tabControl1Item1">
  17. <DataGrid ItemsSource="{Binding Path=Accounts}" IsReadOnly="True"></DataGrid>
  18. </TabItem>
  19. <TabItem Header="tabControl1Item2">
  20. <DataGrid ItemsSource="{Binding Path=Accounts2}" IsReadOnly="True"></DataGrid>
  21. </TabItem>
  22. <TabItem Header="tabControl1Item3">
  23. <DataGrid ItemsSource="{Binding Path=Accounts3}" IsReadOnly="True"></DataGrid>
  24. </TabItem>
  25. </wpfLibrary:TabControlDragable>
  26. <wpfLibrary:TabControlDragable Grid.Row="1" x:Name="tabControl2">
  27. <TabItem Header="tabControl2Item1">
  28. <DataGrid ItemsSource="{Binding Path=Accounts}" IsReadOnly="True"></DataGrid>
  29. </TabItem>
  30. <TabItem Header="tabControl2Item2">
  31. <DataGrid ItemsSource="{Binding Path=Accounts2}" IsReadOnly="True"></DataGrid>
  32. </TabItem>
  33. <TabItem Header="tabControl2Item3">
  34. <DataGrid ItemsSource="{Binding Path=Accounts3}" IsReadOnly="True"></DataGrid>
  35. </TabItem>
  36. </wpfLibrary:TabControlDragable>
  37. </Grid>
  38. </Window>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/632893
推荐阅读
相关标签
  

闽ICP备14008679号