ItemsControlDragHelper.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. using System;
  2. using System.Windows.Controls;
  3. using System.Windows;
  4. using System.Windows.Documents;
  5. using System.Windows.Input;
  6. using System.Windows.Media;
  7. using System.Windows.Shapes;
  8. namespace FlowWorkEditDemo
  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. if (source == null)
  160. throw new ArgumentNullException("source");
  161. _dragEnter = new DragEventHandler(DragScope_DragEnter);
  162. _dragOver = new DragEventHandler(DragScope_DragOver);
  163. _dragLeave = new DragEventHandler(DragScope_DragLeave);
  164. _source = source;
  165. _dragScope = dragScope;
  166. _source.PreviewMouseLeftButtonDown += SourceMouseLeftButtonDown;
  167. _source.PreviewMouseMove += SourceMouseMove;
  168. }
  169. private void SourceMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  170. {
  171. var point = e.GetPosition(_source);
  172. if (!IsMouseOverScrollbar(point))
  173. _mouseDown = point;
  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. var input = adornerSource as IInputElement;
  184. if (input != null)
  185. _offset = e.GetPosition(input);
  186. else
  187. _offset = new Point(0, 0);
  188. object subj = e.OriginalSource;
  189. var fe = adornerSource as FrameworkElement;
  190. if (fe != null && fe.Tag != null)
  191. subj = fe.Tag;
  192. DoDragDrop(subj, adornerSource);
  193. e.Handled = true;
  194. }
  195. }
  196. else
  197. _mouseDown = null;
  198. }
  199. private bool HasMoved(Point point)
  200. {
  201. return Math.Abs(point.X - _mouseDown.Value.X) > SystemParameters.MinimumHorizontalDragDistance / 2
  202. || Math.Abs(point.Y - _mouseDown.Value.Y) > SystemParameters.MinimumVerticalDragDistance / 2;
  203. }
  204. private bool IsMouseOverScrollbar(Point ptMouse)
  205. {
  206. HitTestResult res = VisualTreeHelper.HitTest(_source, ptMouse);
  207. if (res == null)
  208. return false;
  209. DependencyObject depObj = res.VisualHit;
  210. while (depObj != null)
  211. {
  212. if (depObj is System.Windows.Controls.Primitives.ScrollBar)
  213. return true;
  214. // VisualTreeHelper works with objects of type Visual or Visual3D.
  215. // If the current object is not derived from Visual or Visual3D,
  216. // then use the LogicalTreeHelper to find the parent element.
  217. if (depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D)
  218. depObj = VisualTreeHelper.GetParent(depObj);
  219. else
  220. depObj = LogicalTreeHelper.GetParent(depObj);
  221. }
  222. return false;
  223. }
  224. private void DoDragDrop(object dragItem, UIElement adornerSource)
  225. {
  226. if (adornerSource != null)
  227. {
  228. var rect = VisualTreeHelper.GetDescendantBounds(adornerSource);
  229. var size = new Size((double)Math.Ceiling(rect.Width), (double)Math.Ceiling(rect.Height));
  230. var brush = new VisualBrush(adornerSource);
  231. _adorner = new DragDropAdorner(_dragScope, size, brush);
  232. _adorner.Opacity = 0.7;
  233. _adorner.Visibility = Visibility.Hidden;
  234. }
  235. DragDrop.AddPreviewDragEnterHandler(_dragScope, _dragEnter);
  236. DragDrop.AddPreviewDragOverHandler(_dragScope, _dragOver);
  237. DragDrop.AddPreviewDragLeaveHandler(_dragScope, _dragLeave);
  238. var resultEffects = DragDrop.DoDragDrop(_source, dragItem, DragDropEffects.All);
  239. DragFinished(resultEffects);
  240. DragDrop.RemovePreviewDragEnterHandler(_dragScope, _dragEnter);
  241. DragDrop.RemovePreviewDragOverHandler(_dragScope, _dragOver);
  242. DragDrop.RemovePreviewDragLeaveHandler(_dragScope, _dragLeave);
  243. }
  244. private void DragScope_DragEnter(object sender, DragEventArgs args)
  245. {
  246. if (_adorner != null)
  247. AdornerLayer.GetAdornerLayer(_source).Add(_adorner);
  248. }
  249. private void DragScope_DragOver(object sender, DragEventArgs args)
  250. {
  251. if (_adorner != null)
  252. {
  253. var pos = args.GetPosition(_dragScope);
  254. _adorner.SetOffsets(pos.X - _offset.X, pos.Y - _offset.Y);
  255. _adorner.Visibility = Visibility.Visible;
  256. }
  257. }
  258. private void DragScope_DragLeave(object sender, DragEventArgs args)
  259. {
  260. if (_adorner != null)
  261. AdornerLayer.GetAdornerLayer(_source).Remove(_adorner);
  262. }
  263. protected void DragFinished(DragDropEffects ret)
  264. {
  265. if (_adorner != null)
  266. AdornerLayer.GetAdornerLayer(_source).Remove(_adorner);
  267. _adorner = null;
  268. }
  269. #region IDisposable Members
  270. public void Dispose()
  271. {
  272. _source.MouseLeftButtonDown -= SourceMouseLeftButtonDown;
  273. _source.MouseMove -= SourceMouseMove;
  274. }
  275. #endregion
  276. }
  277. }