ListBoxDragDropManager.cs 24 KB

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