ListViewDragDropManager.cs 24 KB

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