ItemsDraggable.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using System.Collections;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Input;
  5. using System.Windows.Media;
  6. namespace Waaagh.Behaviors;
  7. sealed public class ItemsDraggable {
  8. #region Drag Drop Data Format String
  9. static readonly string DraggableDataFormat = "DraggableData";
  10. static readonly string DraggableCollectionFormat = "DraggableCollection";
  11. #endregion
  12. #region Dragging Data Object
  13. private static DataObject? GetDraggingData(DependencyObject obj) {
  14. return (DataObject?)obj.GetValue(DraggingDataProperty);
  15. }
  16. private static void SetDraggingData(DependencyObject obj, DataObject? value) {
  17. obj.SetValue(DraggingDataProperty, value);
  18. }
  19. private static readonly DependencyProperty DraggingDataProperty = DependencyProperty.RegisterAttached(
  20. "DraggingData", typeof(DataObject), typeof(ItemsDraggable), new PropertyMetadata(null));
  21. #endregion
  22. #region Enabled
  23. static public bool GetEnabled(DependencyObject obj) {
  24. return (bool)obj.GetValue(EnabledProperty);
  25. }
  26. static public void SetEnabled(DependencyObject obj, bool value) {
  27. obj.SetValue(EnabledProperty, value);
  28. }
  29. static public readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached(
  30. "Enabled", typeof(bool), typeof(ItemsDraggable), new PropertyMetadata(false, OnEnabledChanged));
  31. static private void OnEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) {
  32. if (sender is ItemsControl itemsControl == false) {
  33. throw new InvalidOperationException("The attached control is not an 'ItemsControl'.");
  34. }
  35. // if (itemsControl.ItemsSource is IList == false) {
  36. // throw new InvalidOperationException("The attached control must have an 'ItemsSource' implements 'IList'.");
  37. // }
  38. itemsControl.AllowDrop = false;
  39. itemsControl.PreviewMouseLeftButtonDown -= ItemsDraggable_PreviewMouseLeftButtonDown;
  40. itemsControl.MouseMove -= ItemsDraggable_MouseMove;
  41. itemsControl.DragEnter -= ItemsDraggable_DragEnter;
  42. itemsControl.DragOver -= ItemsDraggable_DragOver;
  43. itemsControl.DragLeave -= ItemsDraggable_DragLeave;
  44. itemsControl.Drop -= ItemsDraggable_Drop;
  45. if (GetEnabled(itemsControl) == true) {
  46. itemsControl.AllowDrop = true;
  47. itemsControl.PreviewMouseLeftButtonDown += ItemsDraggable_PreviewMouseLeftButtonDown;
  48. itemsControl.MouseMove += ItemsDraggable_MouseMove;
  49. itemsControl.DragEnter += ItemsDraggable_DragEnter;
  50. itemsControl.DragOver += ItemsDraggable_DragOver;
  51. itemsControl.DragLeave += ItemsDraggable_DragLeave;
  52. itemsControl.Drop += ItemsDraggable_Drop;
  53. }
  54. }
  55. #endregion
  56. #region Drag Drop Event
  57. private static void ItemsDraggable_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs args) {
  58. if (sender is ItemsControl control == false) {
  59. return;
  60. }
  61. if (control.ItemsSource is IList == false) {
  62. return;
  63. }
  64. if (GetElement(control, args.GetPosition(control)) is Control sourceControl == false) {
  65. return;
  66. }
  67. if (GetParent(sourceControl) is ItemsControl sourceItemsControl == false) {
  68. return;
  69. }
  70. DataObject data = new DataObject();
  71. data.SetData(DraggableDataFormat, sourceControl.DataContext);
  72. data.SetData(DraggableCollectionFormat, sourceItemsControl.ItemsSource);
  73. SetDraggingData(control, data);
  74. }
  75. static private void ItemsDraggable_MouseMove(object sender, MouseEventArgs args) {
  76. if (args.LeftButton != MouseButtonState.Pressed) {
  77. return;
  78. }
  79. if (sender is ItemsControl source == false) {
  80. return;
  81. }
  82. if (GetDraggingData(source) is DataObject data == false) {
  83. return;
  84. }
  85. SetDraggingData(source, null);
  86. DragDrop.DoDragDrop(source, data, DragDropEffects.Move);
  87. }
  88. static private void ItemsDraggable_DragEnter(object sender, DragEventArgs args) {
  89. }
  90. static private void ItemsDraggable_DragOver(object sender, DragEventArgs args) {
  91. }
  92. static private void ItemsDraggable_DragLeave(object sender, DragEventArgs args) {
  93. }
  94. static private void ItemsDraggable_Drop(object sender, DragEventArgs args) {
  95. if (args.Effects == DragDropEffects.None) {
  96. return;
  97. }
  98. if (sender is ItemsControl control == false) {
  99. return;
  100. }
  101. // 获取 放置目标
  102. if (GetElement(control, args.GetPosition(control)) is Control targetControl == false) {
  103. return;
  104. }
  105. if (GetParent(targetControl) is ItemsControl targetItemsControl == false) {
  106. return;
  107. }
  108. object? targetData = targetControl.DataContext;
  109. IList? targetCollection = targetItemsControl.ItemsSource as IList;
  110. if (targetCollection == null) {
  111. return;
  112. }
  113. // 获取 拖拽源
  114. object? sourceData = args.Data.GetData(DraggableDataFormat) as object;
  115. IList? sourceCollection = args.Data.GetData(DraggableCollectionFormat) as IList;
  116. if (sourceData == null || sourceCollection == null) {
  117. return;
  118. }
  119. // 执行 拖拽影响
  120. DoDragDropEffects(control, args.Effects, sourceCollection, sourceData, targetCollection, targetData);
  121. }
  122. #endregion
  123. #region Drag Drop Effects
  124. public delegate void DoEffectsCallbackHandler(object sender, DoEffectsCallbackArgs args);
  125. public class DoEffectsCallbackArgs {
  126. public IList SourceCollection { get; }
  127. public object SourceData { get; }
  128. public IList TargetCollection { get; }
  129. public object? TargetData { get; }
  130. public DoEffectsCallbackArgs(IList sourceCollection, object sourceData, IList targetCollection, object? targetData) {
  131. SourceCollection = sourceCollection;
  132. SourceData = sourceData;
  133. TargetCollection = targetCollection;
  134. TargetData = targetData;
  135. }
  136. }
  137. static private void DoDragDropEffects(ItemsControl control, DragDropEffects effects, IList sourceCollection, object sourceData, IList targetCollection, object? targetData) {
  138. if (sourceCollection == null || sourceData == null || targetCollection == null) {
  139. return;
  140. }
  141. if (object.ReferenceEquals(sourceData, targetData)) {
  142. return;
  143. }
  144. switch (effects) {
  145. case DragDropEffects.Scroll: {
  146. break;
  147. }
  148. case DragDropEffects.All: {
  149. break;
  150. }
  151. case DragDropEffects.Copy: {
  152. break;
  153. }
  154. case DragDropEffects.Move: {
  155. GetDoMoveEffect(control)?.Invoke(control, new DoEffectsCallbackArgs(sourceCollection, sourceData, targetCollection, targetData));
  156. break;
  157. }
  158. case DragDropEffects.Link: {
  159. break;
  160. }
  161. default: {
  162. break;
  163. }
  164. }
  165. }
  166. #region Do Move Effect Callback
  167. public static DoEffectsCallbackHandler GetDoMoveEffect(DependencyObject obj) {
  168. return (DoEffectsCallbackHandler)obj.GetValue(DoMoveEffectProperty);
  169. }
  170. public static void SetDoMoveEffect(DependencyObject obj, DoEffectsCallbackHandler value) {
  171. obj.SetValue(DoMoveEffectProperty, value);
  172. }
  173. public static readonly DependencyProperty DoMoveEffectProperty = DependencyProperty.RegisterAttached(
  174. "DoMoveEffect", typeof(DoEffectsCallbackHandler), typeof(ItemsDraggable), new PropertyMetadata(null));
  175. #endregion
  176. #endregion
  177. #region Helper
  178. static private Control? GetElement(ItemsControl source, Point point) {
  179. if (GetItemContainerType(source) is Type type == false) {
  180. return null;
  181. }
  182. DependencyObject? dp = source.InputHitTest(point) as DependencyObject;
  183. while (dp != null) {
  184. if (dp.GetType().Equals(type)) {
  185. break;
  186. }
  187. dp = VisualTreeHelper.GetParent(dp);
  188. }
  189. return dp as Control;
  190. }
  191. static private ItemsControl? GetParent(Control control) {
  192. for (DependencyObject dp = VisualTreeHelper.GetParent(control); dp != null; dp = VisualTreeHelper.GetParent(dp)) {
  193. if (dp is ItemsControl parent && parent.ItemContainerGenerator.ContainerFromItem(control.DataContext) != null) {
  194. return parent;
  195. }
  196. }
  197. return null;
  198. }
  199. static private Type? GetItemContainerType(ItemsControl source) {
  200. if (source == null) {
  201. return null;
  202. }
  203. if (source is ListView) {
  204. return typeof(ListViewItem);
  205. }
  206. else if (source is ListBox) {
  207. return typeof(ListBoxItem);
  208. }
  209. else if (source is TreeView || source is TreeViewItem) {
  210. return typeof(TreeViewItem);
  211. }
  212. // else if (source is GridView) {
  213. // return typeof(GridViewColumn);
  214. // }
  215. if (source.Items.Count > 0) {
  216. return source.ItemContainerGenerator.ContainerFromIndex(0)?.GetType();
  217. }
  218. return null;
  219. }
  220. #endregion
  221. }