ItemsControlDragHelper.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. using System;
  2. using System.Windows;
  3. using System.Windows.Input;
  4. using System.Windows.Media;
  5. using System.Windows.Shapes;
  6. using System.Windows.Controls;
  7. using System.Windows.Documents;
  8. namespace Flowchart
  9. {
  10. /// <summary>
  11. /// Helper class used to start drag&drop operation and display a dragging object
  12. /// </summary>
  13. public class ItemsControlDragHelper : IDisposable
  14. {
  15. #region DragDropAdorner
  16. /// <summary>
  17. /// Renders a visual which can follow the mouse cursor,
  18. /// such as during a drag-and-drop operation.
  19. /// </summary>
  20. // Copyright (C) Josh Smith - January 2007
  21. private class DragDropAdorner : Adorner
  22. {
  23. #region Data
  24. private Rectangle child = null;
  25. private double offsetLeft = 0;
  26. private double offsetTop = 0;
  27. #endregion // Data
  28. #region Constructor
  29. /// <summary>
  30. /// Initializes a new instance of DragVisualAdorner.
  31. /// </summary>
  32. /// <param name="adornedElement">The element being adorned.</param>
  33. /// <param name="size">The size of the adorner.</param>
  34. /// <param name="brush">A brush to with which to paint the adorner.</param>
  35. public DragDropAdorner(UIElement adornedElement, Size size, Brush brush)
  36. : base(adornedElement)
  37. {
  38. Rectangle rect = new Rectangle();
  39. rect.Fill = brush;
  40. rect.Width = size.Width;
  41. rect.Height = size.Height;
  42. rect.IsHitTestVisible = false;
  43. this.child = rect;
  44. }
  45. #endregion // Constructor
  46. #region Public Interface
  47. #region GetDesiredTransform
  48. /// <summary>
  49. /// Override.
  50. /// </summary>
  51. /// <param name="transform"></param>
  52. /// <returns></returns>
  53. public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
  54. {
  55. GeneralTransformGroup result = new GeneralTransformGroup();
  56. result.Children.Add(base.GetDesiredTransform(transform));
  57. result.Children.Add(new TranslateTransform(this.offsetLeft, this.offsetTop));
  58. return result;
  59. }
  60. #endregion // GetDesiredTransform
  61. #region OffsetLeft
  62. /// <summary>
  63. /// Gets/sets the horizontal offset of the adorner.
  64. /// </summary>
  65. public double OffsetLeft
  66. {
  67. get { return this.offsetLeft; }
  68. set
  69. {
  70. this.offsetLeft = value;
  71. UpdateLocation();
  72. }
  73. }
  74. #endregion // OffsetLeft
  75. #region SetOffsets
  76. /// <summary>
  77. /// Updates the location of the adorner in one atomic operation.
  78. /// </summary>
  79. /// <param name="left"></param>
  80. /// <param name="top"></param>
  81. public void SetOffsets(double left, double top)
  82. {
  83. this.offsetLeft = left;
  84. this.offsetTop = top;
  85. this.UpdateLocation();
  86. }
  87. #endregion // SetOffsets
  88. #region OffsetTop
  89. /// <summary>
  90. /// Gets/sets the vertical offset of the adorner.
  91. /// </summary>
  92. public double OffsetTop
  93. {
  94. get { return this.offsetTop; }
  95. set
  96. {
  97. this.offsetTop = value;
  98. UpdateLocation();
  99. }
  100. }
  101. #endregion // OffsetTop
  102. #endregion // Public Interface
  103. #region Protected Overrides
  104. /// <summary>
  105. /// Override.
  106. /// </summary>
  107. /// <param name="constraint"></param>
  108. /// <returns></returns>
  109. protected override Size MeasureOverride(Size constraint)
  110. {
  111. this.child.Measure(constraint);
  112. return this.child.DesiredSize;
  113. }
  114. /// <summary>
  115. /// Override.
  116. /// </summary>
  117. /// <param name="finalSize"></param>
  118. /// <returns></returns>
  119. protected override Size ArrangeOverride(Size finalSize)
  120. {
  121. this.child.Arrange(new Rect(finalSize));
  122. return finalSize;
  123. }
  124. /// <summary>
  125. /// Override.
  126. /// </summary>
  127. /// <param name="index"></param>
  128. /// <returns></returns>
  129. protected override Visual GetVisualChild(int index)
  130. {
  131. return this.child;
  132. }
  133. /// <summary>
  134. /// Override. Always returns 1.
  135. /// </summary>
  136. protected override int VisualChildrenCount
  137. {
  138. get { return 1; }
  139. }
  140. #endregion // Protected Overrides
  141. #region Private Helpers
  142. private void UpdateLocation()
  143. {
  144. AdornerLayer adornerLayer = this.Parent as AdornerLayer;
  145. if (adornerLayer != null)
  146. adornerLayer.Update(this.AdornedElement);
  147. }
  148. #endregion // Private Helpers
  149. }
  150. #endregion
  151. private Point? _mouseDown;
  152. private Point _offset;
  153. private ItemsControl _source;
  154. private UIElement _dragScope;
  155. private DragDropAdorner _adorner;
  156. private DragEventHandler _dragOver, _dragEnter, _dragLeave;
  157. public ItemsControlDragHelper(ItemsControl source, UIElement dragScope)
  158. {
  159. _dragEnter = new DragEventHandler(DragScope_DragEnter);
  160. _dragOver = new DragEventHandler(DragScope_DragOver);
  161. _dragLeave = new DragEventHandler(DragScope_DragLeave);
  162. _source = source ?? throw new ArgumentNullException(nameof(source));
  163. _dragScope = dragScope;
  164. _source.PreviewMouseLeftButtonDown += SourceMouseLeftButtonDown;
  165. _source.PreviewMouseMove += SourceMouseMove;
  166. }
  167. private void SourceMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  168. {
  169. var point = e.GetPosition(_source);
  170. if (!IsMouseOverScrollbar(point))
  171. {
  172. _mouseDown = point;
  173. }
  174. }
  175. private void SourceMouseMove(object sender, MouseEventArgs e)
  176. {
  177. if (e.LeftButton == MouseButtonState.Pressed)
  178. {
  179. if (_mouseDown.HasValue && HasMoved(e.GetPosition(_source)))
  180. {
  181. var adornerSource = e.OriginalSource as UIElement;
  182. adornerSource = _source.ContainerFromElement(adornerSource) as UIElement;
  183. _offset = adornerSource is IInputElement input ? e.GetPosition(input) : new Point(0, 0);
  184. object subj = e.OriginalSource;
  185. if (adornerSource is FrameworkElement fe && fe.Tag != null)
  186. subj = fe.Tag;
  187. DoDragDrop(subj, adornerSource);
  188. e.Handled = true;
  189. }
  190. }
  191. else
  192. {
  193. _mouseDown = null;
  194. }
  195. }
  196. private bool HasMoved(Point point)
  197. {
  198. return Math.Abs(point.X - _mouseDown.Value.X) > SystemParameters.MinimumHorizontalDragDistance / 2
  199. || Math.Abs(point.Y - _mouseDown.Value.Y) > SystemParameters.MinimumVerticalDragDistance / 2;
  200. }
  201. private bool IsMouseOverScrollbar(Point ptMouse)
  202. {
  203. HitTestResult res = VisualTreeHelper.HitTest(_source, ptMouse);
  204. if (res == null) return false;
  205. DependencyObject depObj = res.VisualHit;
  206. while (depObj != null)
  207. {
  208. if (depObj is System.Windows.Controls.Primitives.ScrollBar)
  209. return true;
  210. // VisualTreeHelper works with objects of type Visual or Visual3D.
  211. // If the current object is not derived from Visual or Visual3D,
  212. // then use the LogicalTreeHelper to find the parent element.
  213. depObj = depObj is Visual or System.Windows.Media.Media3D.Visual3D
  214. ? VisualTreeHelper.GetParent(depObj)
  215. : LogicalTreeHelper.GetParent(depObj);
  216. }
  217. return false;
  218. }
  219. private void DoDragDrop(object dragItem, UIElement adornerSource)
  220. {
  221. if (adornerSource != null)
  222. {
  223. var rect = VisualTreeHelper.GetDescendantBounds(adornerSource);
  224. var size = new Size((double)Math.Ceiling(rect.Width), (double)Math.Ceiling(rect.Height));
  225. var brush = new VisualBrush(adornerSource);
  226. _adorner = new DragDropAdorner(_dragScope, size, brush)
  227. {
  228. Opacity = 0.7,
  229. Visibility = Visibility.Hidden
  230. };
  231. }
  232. DragDrop.AddPreviewDragEnterHandler(_dragScope, _dragEnter);
  233. DragDrop.AddPreviewDragOverHandler(_dragScope, _dragOver);
  234. DragDrop.AddPreviewDragLeaveHandler(_dragScope, _dragLeave);
  235. var resultEffects = DragDrop.DoDragDrop(_source, dragItem, DragDropEffects.All);
  236. DragFinished(resultEffects);
  237. DragDrop.RemovePreviewDragEnterHandler(_dragScope, _dragEnter);
  238. DragDrop.RemovePreviewDragOverHandler(_dragScope, _dragOver);
  239. DragDrop.RemovePreviewDragLeaveHandler(_dragScope, _dragLeave);
  240. }
  241. private void DragScope_DragEnter(object sender, DragEventArgs args)
  242. {
  243. if (_adorner != null)
  244. {
  245. AdornerLayer.GetAdornerLayer(_source).Add(_adorner);
  246. }
  247. }
  248. private void DragScope_DragOver(object sender, DragEventArgs args)
  249. {
  250. if (_adorner != null)
  251. {
  252. var pos = args.GetPosition(_dragScope);
  253. _adorner.SetOffsets(pos.X - _offset.X, pos.Y - _offset.Y);
  254. _adorner.Visibility = Visibility.Visible;
  255. }
  256. }
  257. private void DragScope_DragLeave(object sender, DragEventArgs args)
  258. {
  259. if (_adorner != null)
  260. {
  261. AdornerLayer.GetAdornerLayer(_source).Remove(_adorner);
  262. }
  263. }
  264. protected void DragFinished(DragDropEffects ret)
  265. {
  266. if (_adorner != null)
  267. {
  268. AdornerLayer.GetAdornerLayer(_source).Remove(_adorner);
  269. }
  270. _adorner = null;
  271. }
  272. #region IDisposable Members
  273. public void Dispose()
  274. {
  275. _source.MouseLeftButtonDown -= SourceMouseLeftButtonDown;
  276. _source.MouseMove -= SourceMouseMove;
  277. }
  278. #endregion
  279. }
  280. }