ListViewDragDropManager.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. // Copyright (C) Josh Smith - January 2007
  2. using System;
  3. using System.Collections.ObjectModel;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Controls.Primitives;
  7. using System.Windows.Documents;
  8. using System.Windows.Input;
  9. using System.Windows.Media;
  10. using WPF.JoshSmith.Adorners;
  11. using WPF.JoshSmith.Controls.Utilities;
  12. namespace WPF.JoshSmith.ServiceProviders.UI
  13. {
  14. #region ListViewDragDropManager
  15. /// <summary>
  16. /// Manages the dragging and dropping of ListViewItems in a ListView.
  17. /// The ItemType type parameter indicates the type of the objects in
  18. /// the ListView's items source. The ListView's ItemsSource must be
  19. /// set to an instance of ObservableCollection of ItemType, or an
  20. /// Exception will be thrown.
  21. /// </summary>
  22. /// <typeparam name="ItemType">The type of the ListView's items.</typeparam>
  23. public class ListViewDragDropManager<ItemType> where ItemType : class
  24. {
  25. #region Data
  26. bool canInitiateDrag;
  27. DragAdorner dragAdorner;
  28. double dragAdornerOpacity;
  29. int indexToSelect;
  30. bool isDragInProgress;
  31. ItemType itemUnderDragCursor;
  32. ListView listView;
  33. Point ptMouseDown;
  34. bool showDragAdorner;
  35. #endregion // Data
  36. #region Constructors
  37. /// <summary>
  38. /// Initializes a new instance of ListViewDragManager.
  39. /// </summary>
  40. public ListViewDragDropManager()
  41. {
  42. this.canInitiateDrag = false;
  43. this.dragAdornerOpacity = 0.7;
  44. this.indexToSelect = -1;
  45. this.showDragAdorner = true;
  46. }
  47. /// <summary>
  48. /// Initializes a new instance of ListViewDragManager.
  49. /// </summary>
  50. /// <param name="listView"></param>
  51. public ListViewDragDropManager(ListView listView)
  52. : this()
  53. {
  54. this.ListView = listView;
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of ListViewDragManager.
  58. /// </summary>
  59. /// <param name="listView"></param>
  60. /// <param name="dragAdornerOpacity"></param>
  61. public ListViewDragDropManager(ListView listView, double dragAdornerOpacity)
  62. : this(listView)
  63. {
  64. this.DragAdornerOpacity = dragAdornerOpacity;
  65. }
  66. /// <summary>
  67. /// Initializes a new instance of ListViewDragManager.
  68. /// </summary>
  69. /// <param name="listView"></param>
  70. /// <param name="showDragAdorner"></param>
  71. public ListViewDragDropManager(ListView listView, bool showDragAdorner)
  72. : this(listView)
  73. {
  74. this.ShowDragAdorner = showDragAdorner;
  75. }
  76. #endregion // Constructors
  77. #region Public Interface
  78. #region DragAdornerOpacity
  79. /// <summary>
  80. /// Gets/sets the opacity of the drag adorner. This property has no
  81. /// effect if ShowDragAdorner is false. The default value is 0.7
  82. /// </summary>
  83. public double DragAdornerOpacity
  84. {
  85. get { return this.dragAdornerOpacity; }
  86. set
  87. {
  88. if (this.IsDragInProgress)
  89. throw new InvalidOperationException("Cannot set the DragAdornerOpacity property during a drag operation.");
  90. if (value < 0.0 || value > 1.0)
  91. throw new ArgumentOutOfRangeException("DragAdornerOpacity", value, "Must be between 0 and 1.");
  92. this.dragAdornerOpacity = value;
  93. }
  94. }
  95. #endregion // DragAdornerOpacity
  96. #region IsDragInProgress
  97. /// <summary>
  98. /// Returns true if there is currently a drag operation being managed.
  99. /// </summary>
  100. public bool IsDragInProgress
  101. {
  102. get { return this.isDragInProgress; }
  103. private set { this.isDragInProgress = value; }
  104. }
  105. #endregion // IsDragInProgress
  106. #region ListView
  107. /// <summary>
  108. /// Gets/sets the ListView whose dragging is managed. This property
  109. /// can be set to null, to prevent drag management from occuring. If
  110. /// the ListView's AllowDrop property is false, it will be set to true.
  111. /// </summary>
  112. public ListView ListView
  113. {
  114. get { return listView; }
  115. set
  116. {
  117. if (this.IsDragInProgress)
  118. throw new InvalidOperationException("Cannot set the ListView property during a drag operation.");
  119. if (this.listView != null)
  120. {
  121. #region Unhook Events
  122. this.listView.PreviewMouseLeftButtonDown -= listView_PreviewMouseLeftButtonDown;
  123. this.listView.PreviewMouseMove -= listView_PreviewMouseMove;
  124. this.listView.DragOver -= listView_DragOver;
  125. this.listView.DragLeave -= listView_DragLeave;
  126. this.listView.DragEnter -= listView_DragEnter;
  127. this.listView.Drop -= listView_Drop;
  128. #endregion // Unhook Events
  129. }
  130. this.listView = value;
  131. if (this.listView != null)
  132. {
  133. if (!this.listView.AllowDrop)
  134. this.listView.AllowDrop = true;
  135. #region Hook Events
  136. this.listView.PreviewMouseLeftButtonDown += listView_PreviewMouseLeftButtonDown;
  137. this.listView.PreviewMouseMove += listView_PreviewMouseMove;
  138. this.listView.DragOver += listView_DragOver;
  139. this.listView.DragLeave += listView_DragLeave;
  140. this.listView.DragEnter += listView_DragEnter;
  141. this.listView.Drop += listView_Drop;
  142. #endregion // Hook Events
  143. }
  144. }
  145. }
  146. #endregion // ListView
  147. #region ProcessDrop [event]
  148. /// <summary>
  149. /// Raised when a drop occurs. By default the dropped item will be moved
  150. /// to the target index. Handle this event if relocating the dropped item
  151. /// requires custom behavior. Note, if this event is handled the default
  152. /// item dropping logic will not occur.
  153. /// </summary>
  154. public event EventHandler<ProcessDropEventArgs<ItemType>> ProcessDrop;
  155. #endregion // ProcessDrop [event]
  156. #region ShowDragAdorner
  157. /// <summary>
  158. /// Gets/sets whether a visual representation of the ListViewItem being dragged
  159. /// follows the mouse cursor during a drag operation. The default value is true.
  160. /// </summary>
  161. public bool ShowDragAdorner
  162. {
  163. get { return this.showDragAdorner; }
  164. set
  165. {
  166. if (this.IsDragInProgress)
  167. throw new InvalidOperationException("Cannot set the ShowDragAdorner property during a drag operation.");
  168. this.showDragAdorner = value;
  169. }
  170. }
  171. #endregion // ShowDragAdorner
  172. #endregion // Public Interface
  173. #region Event Handling Methods
  174. #region listView_PreviewMouseLeftButtonDown
  175. void listView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  176. {
  177. if (this.IsMouseOverScrollbar)
  178. {
  179. // 4/13/2007 - Set the flag to false when cursor is over scrollbar.
  180. this.canInitiateDrag = false;
  181. return;
  182. }
  183. int index = this.IndexUnderDragCursor;
  184. this.canInitiateDrag = index > -1;
  185. if (this.canInitiateDrag)
  186. {
  187. // Remember the location and index of the ListViewItem the user clicked on for later.
  188. this.ptMouseDown = MouseUtilities.GetMousePosition(this.listView);
  189. this.indexToSelect = index;
  190. }
  191. else
  192. {
  193. this.ptMouseDown = new Point(-10000, -10000);
  194. this.indexToSelect = -1;
  195. }
  196. }
  197. #endregion // listView_PreviewMouseLeftButtonDown
  198. #region listView_PreviewMouseMove
  199. void listView_PreviewMouseMove(object sender, MouseEventArgs e)
  200. {
  201. if (!this.CanStartDragOperation)
  202. return;
  203. // Select the item the user clicked on.
  204. if (this.listView.SelectedIndex != this.indexToSelect)
  205. this.listView.SelectedIndex = this.indexToSelect;
  206. // If the item at the selected index is null, there's nothing
  207. // we can do, so just return;
  208. if (this.listView.SelectedItem == null)
  209. return;
  210. ListViewItem itemToDrag = this.GetListViewItem(this.listView.SelectedIndex);
  211. if (itemToDrag == null)
  212. return;
  213. AdornerLayer adornerLayer = this.ShowDragAdornerResolved ? this.InitializeAdornerLayer(itemToDrag) : null;
  214. this.InitializeDragOperation(itemToDrag);
  215. this.PerformDragOperation();
  216. this.FinishDragOperation(itemToDrag, adornerLayer);
  217. }
  218. #endregion // listView_PreviewMouseMove
  219. #region listView_DragOver
  220. void listView_DragOver(object sender, DragEventArgs e)
  221. {
  222. e.Effects = DragDropEffects.Move;
  223. if (this.ShowDragAdornerResolved)
  224. this.UpdateDragAdornerLocation();
  225. // Update the item which is known to be currently under the drag cursor.
  226. int index = this.IndexUnderDragCursor;
  227. this.ItemUnderDragCursor = index < 0 ? null : this.ListView.Items[index] as ItemType;
  228. }
  229. #endregion // listView_DragOver
  230. #region listView_DragLeave
  231. void listView_DragLeave(object sender, DragEventArgs e)
  232. {
  233. if (!this.IsMouseOver(this.listView))
  234. {
  235. if (this.ItemUnderDragCursor != null)
  236. this.ItemUnderDragCursor = null;
  237. if (this.dragAdorner != null)
  238. this.dragAdorner.Visibility = Visibility.Collapsed;
  239. }
  240. }
  241. #endregion // listView_DragLeave
  242. #region listView_DragEnter
  243. void listView_DragEnter(object sender, DragEventArgs e)
  244. {
  245. if (this.dragAdorner != null && this.dragAdorner.Visibility != Visibility.Visible)
  246. {
  247. // Update the location of the adorner and then show it.
  248. this.UpdateDragAdornerLocation();
  249. this.dragAdorner.Visibility = Visibility.Visible;
  250. }
  251. }
  252. #endregion // listView_DragEnter
  253. #region listView_Drop
  254. void listView_Drop(object sender, DragEventArgs e)
  255. {
  256. if (this.ItemUnderDragCursor != null)
  257. this.ItemUnderDragCursor = null;
  258. e.Effects = DragDropEffects.None;
  259. if (!e.Data.GetDataPresent(typeof(ItemType)))
  260. return;
  261. // Get the data object which was dropped.
  262. ItemType data = e.Data.GetData(typeof(ItemType)) as ItemType;
  263. if (data == null)
  264. return;
  265. // Get the ObservableCollection<ItemType> which contains the dropped data object.
  266. ObservableCollection<ItemType> itemsSource = this.listView.ItemsSource as ObservableCollection<ItemType>;
  267. if (itemsSource == null)
  268. throw new Exception(
  269. "A ListView managed by ListViewDragManager must have its ItemsSource set to an ObservableCollection<ItemType>.");
  270. int oldIndex = itemsSource.IndexOf(data);
  271. int newIndex = this.IndexUnderDragCursor;
  272. if (newIndex < 0)
  273. {
  274. // The drag started somewhere else, and our ListView is empty
  275. // so make the new item the first in the list.
  276. if (itemsSource.Count == 0)
  277. newIndex = 0;
  278. // The drag started somewhere else, but our ListView has items
  279. // so make the new item the last in the list.
  280. else if (oldIndex < 0)
  281. newIndex = itemsSource.Count;
  282. // The user is trying to drop an item from our ListView into
  283. // our ListView, but the mouse is not over an item, so don't
  284. // let them drop it.
  285. else
  286. return;
  287. }
  288. // Dropping an item back onto itself is not considered an actual 'drop'.
  289. if (oldIndex == newIndex)
  290. return;
  291. if (this.ProcessDrop != null)
  292. {
  293. // Let the client code process the drop.
  294. ProcessDropEventArgs<ItemType> args = new ProcessDropEventArgs<ItemType>(itemsSource, data, oldIndex, newIndex, e.AllowedEffects);
  295. this.ProcessDrop(this, args);
  296. e.Effects = args.Effects;
  297. }
  298. else
  299. {
  300. // Move the dragged data object from it's original index to the
  301. // new index (according to where the mouse cursor is). If it was
  302. // not previously in the ListBox, then insert the item.
  303. if (oldIndex > -1)
  304. itemsSource.Move(oldIndex, newIndex);
  305. else
  306. itemsSource.Insert(newIndex, data);
  307. // Set the Effects property so that the call to DoDragDrop will return 'Move'.
  308. e.Effects = DragDropEffects.Move;
  309. }
  310. }
  311. #endregion // listView_Drop
  312. #endregion // Event Handling Methods
  313. #region Private Helpers
  314. #region CanStartDragOperation
  315. bool CanStartDragOperation
  316. {
  317. get
  318. {
  319. if (Mouse.LeftButton != MouseButtonState.Pressed)
  320. return false;
  321. if (!this.canInitiateDrag)
  322. return false;
  323. if (this.indexToSelect == -1)
  324. return false;
  325. if (!this.HasCursorLeftDragThreshold)
  326. return false;
  327. return true;
  328. }
  329. }
  330. #endregion // CanStartDragOperation
  331. #region FinishDragOperation
  332. void FinishDragOperation(ListViewItem draggedItem, AdornerLayer adornerLayer)
  333. {
  334. // Let the ListViewItem know that it is not being dragged anymore.
  335. ListViewItemDragState.SetIsBeingDragged(draggedItem, false);
  336. this.IsDragInProgress = false;
  337. if (this.ItemUnderDragCursor != null)
  338. this.ItemUnderDragCursor = null;
  339. // Remove the drag adorner from the adorner layer.
  340. if (adornerLayer != null)
  341. {
  342. adornerLayer.Remove(this.dragAdorner);
  343. this.dragAdorner = null;
  344. }
  345. }
  346. #endregion // FinishDragOperation
  347. #region GetListViewItem
  348. ListViewItem GetListViewItem(int index)
  349. {
  350. if (this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
  351. return null;
  352. return this.listView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
  353. }
  354. ListViewItem GetListViewItem(ItemType dataItem)
  355. {
  356. if (this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
  357. return null;
  358. return this.listView.ItemContainerGenerator.ContainerFromItem(dataItem) as ListViewItem;
  359. }
  360. #endregion // GetListViewItem
  361. #region HasCursorLeftDragThreshold
  362. bool HasCursorLeftDragThreshold
  363. {
  364. get
  365. {
  366. if (this.indexToSelect < 0)
  367. return false;
  368. ListViewItem item = this.GetListViewItem(this.indexToSelect);
  369. Rect bounds = VisualTreeHelper.GetDescendantBounds(item);
  370. Point ptInItem = this.listView.TranslatePoint(this.ptMouseDown, item);
  371. // In case the cursor is at the very top or bottom of the ListViewItem
  372. // we want to make the vertical threshold very small so that dragging
  373. // over an adjacent item does not select it.
  374. double topOffset = Math.Abs(ptInItem.Y);
  375. double btmOffset = Math.Abs(bounds.Height - ptInItem.Y);
  376. double vertOffset = Math.Min(topOffset, btmOffset);
  377. double width = SystemParameters.MinimumHorizontalDragDistance * 2;
  378. double height = Math.Min(SystemParameters.MinimumVerticalDragDistance, vertOffset) * 2;
  379. Size szThreshold = new Size(width, height);
  380. Rect rect = new Rect(this.ptMouseDown, szThreshold);
  381. rect.Offset(szThreshold.Width / -2, szThreshold.Height / -2);
  382. Point ptInListView = MouseUtilities.GetMousePosition(this.listView);
  383. return !rect.Contains(ptInListView);
  384. }
  385. }
  386. #endregion // HasCursorLeftDragThreshold
  387. #region IndexUnderDragCursor
  388. /// <summary>
  389. /// Returns the index of the ListViewItem underneath the
  390. /// drag cursor, or -1 if the cursor is not over an item.
  391. /// </summary>
  392. int IndexUnderDragCursor
  393. {
  394. get
  395. {
  396. int index = -1;
  397. for (int i = 0; i < this.listView.Items.Count; ++i)
  398. {
  399. ListViewItem item = this.GetListViewItem(i);
  400. if (this.IsMouseOver(item))
  401. {
  402. index = i;
  403. break;
  404. }
  405. }
  406. return index;
  407. }
  408. }
  409. #endregion // IndexUnderDragCursor
  410. #region InitializeAdornerLayer
  411. AdornerLayer InitializeAdornerLayer(ListViewItem itemToDrag)
  412. {
  413. // Create a brush which will paint the ListViewItem onto
  414. // a visual in the adorner layer.
  415. VisualBrush brush = new VisualBrush(itemToDrag);
  416. // Create an element which displays the source item while it is dragged.
  417. this.dragAdorner = new DragAdorner(this.listView, itemToDrag.RenderSize, brush);
  418. // Set the drag adorner's opacity.
  419. this.dragAdorner.Opacity = this.DragAdornerOpacity;
  420. AdornerLayer layer = AdornerLayer.GetAdornerLayer(this.listView);
  421. layer.Add(dragAdorner);
  422. // Save the location of the cursor when the left mouse button was pressed.
  423. this.ptMouseDown = MouseUtilities.GetMousePosition(this.listView);
  424. return layer;
  425. }
  426. #endregion // InitializeAdornerLayer
  427. #region InitializeDragOperation
  428. void InitializeDragOperation(ListViewItem itemToDrag)
  429. {
  430. // Set some flags used during the drag operation.
  431. this.IsDragInProgress = true;
  432. this.canInitiateDrag = false;
  433. // Let the ListViewItem know that it is being dragged.
  434. ListViewItemDragState.SetIsBeingDragged(itemToDrag, true);
  435. }
  436. #endregion // InitializeDragOperation
  437. #region IsMouseOver
  438. bool IsMouseOver(Visual target)
  439. {
  440. // We need to use MouseUtilities to figure out the cursor
  441. // coordinates because, during a drag-drop operation, the WPF
  442. // mechanisms for getting the coordinates behave strangely.
  443. Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
  444. Point mousePos = MouseUtilities.GetMousePosition(target);
  445. return bounds.Contains(mousePos);
  446. }
  447. #endregion // IsMouseOver
  448. #region IsMouseOverScrollbar
  449. /// <summary>
  450. /// Returns true if the mouse cursor is over a scrollbar in the ListView.
  451. /// </summary>
  452. bool IsMouseOverScrollbar
  453. {
  454. get
  455. {
  456. Point ptMouse = MouseUtilities.GetMousePosition(this.listView);
  457. HitTestResult res = VisualTreeHelper.HitTest(this.listView, ptMouse);
  458. if (res == null)
  459. return false;
  460. DependencyObject depObj = res.VisualHit;
  461. while (depObj != null)
  462. {
  463. if (depObj is ScrollBar)
  464. return true;
  465. // VisualTreeHelper works with objects of type Visual or Visual3D.
  466. // If the current object is not derived from Visual or Visual3D,
  467. // then use the LogicalTreeHelper to find the parent element.
  468. if (depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D)
  469. depObj = VisualTreeHelper.GetParent(depObj);
  470. else
  471. depObj = LogicalTreeHelper.GetParent(depObj);
  472. }
  473. return false;
  474. }
  475. }
  476. #endregion // IsMouseOverScrollbar
  477. #region ItemUnderDragCursor
  478. ItemType ItemUnderDragCursor
  479. {
  480. get { return this.itemUnderDragCursor; }
  481. set
  482. {
  483. if (this.itemUnderDragCursor == value)
  484. return;
  485. // The first pass handles the previous item under the cursor.
  486. // The second pass handles the new one.
  487. for (int i = 0; i < 2; ++i)
  488. {
  489. if (i == 1)
  490. this.itemUnderDragCursor = value;
  491. if (this.itemUnderDragCursor != null)
  492. {
  493. ListViewItem listViewItem = this.GetListViewItem(this.itemUnderDragCursor);
  494. if (listViewItem != null)
  495. ListViewItemDragState.SetIsUnderDragCursor(listViewItem, i == 1);
  496. }
  497. }
  498. }
  499. }
  500. #endregion // ItemUnderDragCursor
  501. #region PerformDragOperation
  502. void PerformDragOperation()
  503. {
  504. ItemType selectedItem = this.listView.SelectedItem as ItemType;
  505. DragDropEffects allowedEffects = DragDropEffects.Move | DragDropEffects.Move | DragDropEffects.Link;
  506. if (DragDrop.DoDragDrop(this.listView, selectedItem, allowedEffects) != DragDropEffects.None)
  507. {
  508. // The item was dropped into a new location,
  509. // so make it the new selected item.
  510. this.listView.SelectedItem = selectedItem;
  511. }
  512. }
  513. #endregion // PerformDragOperation
  514. #region ShowDragAdornerResolved
  515. bool ShowDragAdornerResolved
  516. {
  517. get { return this.ShowDragAdorner && this.DragAdornerOpacity > 0.0; }
  518. }
  519. #endregion // ShowDragAdornerResolved
  520. #region UpdateDragAdornerLocation
  521. void UpdateDragAdornerLocation()
  522. {
  523. if (this.dragAdorner != null)
  524. {
  525. Point ptCursor = MouseUtilities.GetMousePosition(this.ListView);
  526. double left = ptCursor.X - this.ptMouseDown.X;
  527. // 4/13/2007 - Made the top offset relative to the item being dragged.
  528. ListViewItem itemBeingDragged = this.GetListViewItem(this.indexToSelect);
  529. Point itemLoc = itemBeingDragged.TranslatePoint(new Point(0, 0), this.ListView);
  530. double top = itemLoc.Y + ptCursor.Y - this.ptMouseDown.Y;
  531. this.dragAdorner.SetOffsets(left, top);
  532. }
  533. }
  534. #endregion // UpdateDragAdornerLocation
  535. #endregion // Private Helpers
  536. }
  537. #endregion // ListViewDragDropManager
  538. #region ListViewItemDragState
  539. /// <summary>
  540. /// Exposes attached properties used in conjunction with the ListViewDragDropManager class.
  541. /// Those properties can be used to allow triggers to modify the appearance of ListViewItems
  542. /// in a ListView during a drag-drop operation.
  543. /// </summary>
  544. public static class ListViewItemDragState
  545. {
  546. #region IsBeingDragged
  547. /// <summary>
  548. /// Identifies the ListViewItemDragState's IsBeingDragged attached property.
  549. /// This field is read-only.
  550. /// </summary>
  551. public static readonly DependencyProperty IsBeingDraggedProperty =
  552. DependencyProperty.RegisterAttached(
  553. "IsBeingDragged",
  554. typeof(bool),
  555. typeof(ListViewItemDragState),
  556. new UIPropertyMetadata(false));
  557. /// <summary>
  558. /// Returns true if the specified ListViewItem is being dragged, else false.
  559. /// </summary>
  560. /// <param name="item">The ListViewItem to check.</param>
  561. public static bool GetIsBeingDragged(ListViewItem item)
  562. {
  563. return (bool)item.GetValue(IsBeingDraggedProperty);
  564. }
  565. /// <summary>
  566. /// Sets the IsBeingDragged attached property for the specified ListViewItem.
  567. /// </summary>
  568. /// <param name="item">The ListViewItem to set the property on.</param>
  569. /// <param name="value">Pass true if the element is being dragged, else false.</param>
  570. internal static void SetIsBeingDragged(ListViewItem item, bool value)
  571. {
  572. item.SetValue(IsBeingDraggedProperty, value);
  573. }
  574. #endregion // IsBeingDragged
  575. #region IsUnderDragCursor
  576. /// <summary>
  577. /// Identifies the ListViewItemDragState's IsUnderDragCursor attached property.
  578. /// This field is read-only.
  579. /// </summary>
  580. public static readonly DependencyProperty IsUnderDragCursorProperty =
  581. DependencyProperty.RegisterAttached(
  582. "IsUnderDragCursor",
  583. typeof(bool),
  584. typeof(ListViewItemDragState),
  585. new UIPropertyMetadata(false));
  586. /// <summary>
  587. /// Returns true if the specified ListViewItem is currently underneath the cursor
  588. /// during a drag-drop operation, else false.
  589. /// </summary>
  590. /// <param name="item">The ListViewItem to check.</param>
  591. public static bool GetIsUnderDragCursor(ListViewItem item)
  592. {
  593. return (bool)item.GetValue(IsUnderDragCursorProperty);
  594. }
  595. /// <summary>
  596. /// Sets the IsUnderDragCursor attached property for the specified ListViewItem.
  597. /// </summary>
  598. /// <param name="item">The ListViewItem to set the property on.</param>
  599. /// <param name="value">Pass true if the element is underneath the drag cursor, else false.</param>
  600. internal static void SetIsUnderDragCursor(ListViewItem item, bool value)
  601. {
  602. item.SetValue(IsUnderDragCursorProperty, value);
  603. }
  604. #endregion // IsUnderDragCursor
  605. }
  606. #endregion // ListViewItemDragState
  607. #region ProcessDropEventArgs
  608. /// <summary>
  609. /// Event arguments used by the ListViewDragDropManager.ProcessDrop event.
  610. /// </summary>
  611. /// <typeparam name="ItemType">The type of data object being dropped.</typeparam>
  612. public class ProcessDropEventArgs<ItemType> : EventArgs where ItemType : class
  613. {
  614. #region Data
  615. ObservableCollection<ItemType> itemsSource;
  616. ItemType dataItem;
  617. int oldIndex;
  618. int newIndex;
  619. DragDropEffects allowedEffects = DragDropEffects.None;
  620. DragDropEffects effects = DragDropEffects.None;
  621. #endregion // Data
  622. #region Constructor
  623. internal ProcessDropEventArgs(
  624. ObservableCollection<ItemType> itemsSource,
  625. ItemType dataItem,
  626. int oldIndex,
  627. int newIndex,
  628. DragDropEffects allowedEffects)
  629. {
  630. this.itemsSource = itemsSource;
  631. this.dataItem = dataItem;
  632. this.oldIndex = oldIndex;
  633. this.newIndex = newIndex;
  634. this.allowedEffects = allowedEffects;
  635. }
  636. #endregion // Constructor
  637. #region Public Properties
  638. /// <summary>
  639. /// The items source of the ListView where the drop occurred.
  640. /// </summary>
  641. public ObservableCollection<ItemType> ItemsSource
  642. {
  643. get { return this.itemsSource; }
  644. }
  645. /// <summary>
  646. /// The data object which was dropped.
  647. /// </summary>
  648. public ItemType DataItem
  649. {
  650. get { return this.dataItem; }
  651. }
  652. /// <summary>
  653. /// The current index of the data item being dropped, in the ItemsSource collection.
  654. /// </summary>
  655. public int OldIndex
  656. {
  657. get { return this.oldIndex; }
  658. }
  659. /// <summary>
  660. /// The target index of the data item being dropped, in the ItemsSource collection.
  661. /// </summary>
  662. public int NewIndex
  663. {
  664. get { return this.newIndex; }
  665. }
  666. /// <summary>
  667. /// The drag drop effects allowed to be performed.
  668. /// </summary>
  669. public DragDropEffects AllowedEffects
  670. {
  671. get { return allowedEffects; }
  672. }
  673. /// <summary>
  674. /// The drag drop effect(s) performed on the dropped item.
  675. /// </summary>
  676. public DragDropEffects Effects
  677. {
  678. get { return effects; }
  679. set { effects = value; }
  680. }
  681. #endregion // Public Properties
  682. }
  683. #endregion // ProcessDropEventArgs
  684. }