ListBoxDragDropManager.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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. Rect bounds = VisualTreeHelper.GetDescendantBounds( item );
  392. Point ptInItem = this.listBox.TranslatePoint( this.ptMouseDown, item );
  393. // In case the cursor is at the very top or bottom of the ListBoxItem
  394. // we want to make the vertical threshold very small so that dragging
  395. // over an adjacent item does not select it.
  396. double topOffset = Math.Abs( ptInItem.Y );
  397. double btmOffset = Math.Abs( bounds.Height - ptInItem.Y );
  398. double vertOffset = Math.Min( topOffset, btmOffset );
  399. double width = SystemParameters.MinimumHorizontalDragDistance * 2;
  400. double height = Math.Min( SystemParameters.MinimumVerticalDragDistance, vertOffset ) * 2;
  401. Size szThreshold = new Size( width, height );
  402. Rect rect = new Rect( this.ptMouseDown, szThreshold );
  403. rect.Offset( szThreshold.Width / -2, szThreshold.Height / -2 );
  404. //Point ptInListBox = MouseUtil.GetMousePosition(this.listBox);
  405. Point ptInListBox = MouseUtil.GetMousePosition();
  406. return !rect.Contains( ptInListBox );
  407. }
  408. }
  409. #endregion // HasCursorLeftDragThreshold
  410. #region IndexUnderDragCursor
  411. /// <summary>
  412. /// Returns the index of the ListBoxItem underneath the
  413. /// drag cursor, or -1 if the cursor is not over an item.
  414. /// </summary>
  415. int IndexUnderDragCursor
  416. {
  417. get
  418. {
  419. int index = -1;
  420. for( int i = 0; i < this.listBox.Items.Count; ++i )
  421. {
  422. ListBoxItem item = this.GetListBoxItem( i );
  423. if( this.IsMouseOver( item ) )
  424. {
  425. index = i;
  426. break;
  427. }
  428. }
  429. return index;
  430. }
  431. }
  432. #endregion // IndexUnderDragCursor
  433. #region InitializeAdornerLayer
  434. AdornerLayer InitializeAdornerLayer( ListBoxItem itemToDrag )
  435. {
  436. // Create a brush which will paint the ListBoxItem onto
  437. // a visual in the adorner layer.
  438. VisualBrush brush = new VisualBrush( itemToDrag );
  439. // Create an element which displays the source item while it is dragged.
  440. this.dragAdorner = new DragAdorner( this.listBox, itemToDrag.RenderSize, brush );
  441. // Set the drag adorner's opacity.
  442. this.dragAdorner.Opacity = this.DragAdornerOpacity;
  443. AdornerLayer layer = AdornerLayer.GetAdornerLayer( this.listBox );
  444. layer.Add( dragAdorner );
  445. // Save the location of the cursor when the left mouse button was pressed.
  446. this.ptMouseDown = MouseUtil.GetMousePosition(this.listBox);
  447. //this.ptMouseDown = MouseUtil.GetMousePosition();
  448. return layer;
  449. }
  450. #endregion // InitializeAdornerLayer
  451. #region InitializeDragOperation
  452. void InitializeDragOperation( ListBoxItem itemToDrag )
  453. {
  454. // Set some flags used during the drag operation.
  455. this.IsDragInProgress = true;
  456. this.canInitiateDrag = false;
  457. // Let the ListBoxItem know that it is being dragged.
  458. ListBoxItemDragState.SetIsBeingDragged( itemToDrag, true );
  459. }
  460. #endregion // InitializeDragOperation
  461. #region IsMouseOver
  462. bool IsMouseOver( Visual target )
  463. {
  464. // We need to use MouseUtil to figure out the cursor
  465. // coordinates because, during a drag-drop operation, the WPF
  466. // mechanisms for getting the coordinates behave strangely.
  467. Rect bounds = VisualTreeHelper.GetDescendantBounds( target );
  468. Point mousePos = MouseUtil.GetMousePosition( target );
  469. return bounds.Contains( mousePos );
  470. }
  471. #endregion // IsMouseOver
  472. #region IsMouseOverScrollbar
  473. /// <summary>
  474. /// Returns true if the mouse cursor is over a scrollbar in the ListBox.
  475. /// </summary>
  476. bool IsMouseOverScrollbar
  477. {
  478. get
  479. {
  480. Point ptMouse = MouseUtil.GetMousePosition( this.listBox );
  481. HitTestResult res = VisualTreeHelper.HitTest( this.listBox, ptMouse );
  482. if( res == null )
  483. return false;
  484. DependencyObject depObj = res.VisualHit;
  485. while( depObj != null )
  486. {
  487. if( depObj is ScrollBar )
  488. return true;
  489. // VisualTreeHelper works with objects of type Visual or Visual3D.
  490. // If the current object is not derived from Visual or Visual3D,
  491. // then use the LogicalTreeHelper to find the parent element.
  492. if( depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D )
  493. depObj = VisualTreeHelper.GetParent( depObj );
  494. else
  495. depObj = LogicalTreeHelper.GetParent( depObj );
  496. }
  497. return false;
  498. }
  499. }
  500. #endregion // IsMouseOverScrollbar
  501. #region ItemUnderDragCursor
  502. ItemType ItemUnderDragCursor
  503. {
  504. get { return this.itemUnderDragCursor; }
  505. set
  506. {
  507. if( this.itemUnderDragCursor == value )
  508. return;
  509. // The first pass handles the previous item under the cursor.
  510. // The second pass handles the new one.
  511. for( int i = 0; i < 2; ++i )
  512. {
  513. if( i == 1 )
  514. this.itemUnderDragCursor = value;
  515. if( this.itemUnderDragCursor != null )
  516. {
  517. ListBoxItem listBoxItem = this.GetListBoxItem( this.itemUnderDragCursor );
  518. if( listBoxItem != null )
  519. ListBoxItemDragState.SetIsUnderDragCursor( listBoxItem, i == 1 );
  520. }
  521. }
  522. }
  523. }
  524. #endregion // ItemUnderDragCursor
  525. #region PerformDragOperation
  526. void PerformDragOperation()
  527. {
  528. ItemType selectedItem = this.listBox.SelectedItem as ItemType;
  529. DragDropEffects allowedEffects = DragDropEffects.Move | DragDropEffects.Move | DragDropEffects.Link;
  530. if( DragDrop.DoDragDrop( this.listBox, selectedItem, allowedEffects ) != DragDropEffects.None )
  531. {
  532. // The item was dropped into a new location,
  533. // so make it the new selected item.
  534. this.listBox.SelectedItem = selectedItem;
  535. }
  536. }
  537. #endregion // PerformDragOperation
  538. #region ShowDragAdornerResolved
  539. bool ShowDragAdornerResolved
  540. {
  541. get { return this.ShowDragAdorner && this.DragAdornerOpacity > 0.0; }
  542. }
  543. #endregion // ShowDragAdornerResolved
  544. #region UpdateDragAdornerLocation
  545. void UpdateDragAdornerLocation()
  546. {
  547. if( this.dragAdorner != null )
  548. {
  549. //Point ptCursor = MouseUtil.GetMousePosition(this.ListBox);
  550. Point ptCursor = MouseUtil.GetMousePosition(this.ListBox);
  551. //double left = ptCursor.X - this.ptMouseDown.X;
  552. //double left2 = ptCursor.X;
  553. ListBoxItem itemBeingDragged = this.GetListBoxItem(this.indexToSelect);
  554. // 4/13/2007 - Made the top offset relative to the item being dragged.
  555. Point itemLoc = itemBeingDragged.TranslatePoint( new Point( 0, 0 ), this.ListBox );
  556. double top = itemLoc.Y + ptCursor.Y - this.ptMouseDown.Y;
  557. double left = itemLoc.X + ptCursor.X - this.ptMouseDown.X;
  558. this.dragAdorner.SetOffsets(left, top );
  559. }
  560. }
  561. #endregion // UpdateDragAdornerLocation
  562. #endregion // Private Helpers
  563. }
  564. #endregion // ListBoxDragDropManager
  565. #region ListBoxItemDragState
  566. /// <summary>
  567. /// Exposes attached properties used in conjunction with the ListBoxDragDropManager class.
  568. /// Those properties can be used to allow triggers to modify the appearance of ListBoxItems
  569. /// in a ListBox during a drag-drop operation.
  570. /// </summary>
  571. public static class ListBoxItemDragState
  572. {
  573. #region IsBeingDragged
  574. /// <summary>
  575. /// Identifies the ListBoxItemDragState's IsBeingDragged attached property.
  576. /// This field is read-only.
  577. /// </summary>
  578. public static readonly DependencyProperty IsBeingDraggedProperty =
  579. DependencyProperty.RegisterAttached(
  580. "IsBeingDragged",
  581. typeof( bool ),
  582. typeof( ListBoxItemDragState ),
  583. new UIPropertyMetadata( false ) );
  584. /// <summary>
  585. /// Returns true if the specified ListBoxItem is being dragged, else false.
  586. /// </summary>
  587. /// <param name="item">The ListBoxItem to check.</param>
  588. public static bool GetIsBeingDragged( ListBoxItem item )
  589. {
  590. return (bool)item.GetValue( IsBeingDraggedProperty );
  591. }
  592. /// <summary>
  593. /// Sets the IsBeingDragged attached property for the specified ListBoxItem.
  594. /// </summary>
  595. /// <param name="item">The ListBoxItem to set the property on.</param>
  596. /// <param name="value">Pass true if the element is being dragged, else false.</param>
  597. internal static void SetIsBeingDragged( ListBoxItem item, bool value )
  598. {
  599. item.SetValue( IsBeingDraggedProperty, value );
  600. }
  601. #endregion // IsBeingDragged
  602. #region IsUnderDragCursor
  603. /// <summary>
  604. /// Identifies the ListBoxItemDragState's IsUnderDragCursor attached property.
  605. /// This field is read-only.
  606. /// </summary>
  607. public static readonly DependencyProperty IsUnderDragCursorProperty =
  608. DependencyProperty.RegisterAttached(
  609. "IsUnderDragCursor",
  610. typeof( bool ),
  611. typeof( ListBoxItemDragState ),
  612. new UIPropertyMetadata( false ) );
  613. /// <summary>
  614. /// Returns true if the specified ListBoxItem is currently underneath the cursor
  615. /// during a drag-drop operation, else false.
  616. /// </summary>
  617. /// <param name="item">The ListBoxItem to check.</param>
  618. public static bool GetIsUnderDragCursor( ListBoxItem item )
  619. {
  620. return (bool)item.GetValue( IsUnderDragCursorProperty );
  621. }
  622. /// <summary>
  623. /// Sets the IsUnderDragCursor attached property for the specified ListBoxItem.
  624. /// </summary>
  625. /// <param name="item">The ListBoxItem to set the property on.</param>
  626. /// <param name="value">Pass true if the element is underneath the drag cursor, else false.</param>
  627. internal static void SetIsUnderDragCursor( ListBoxItem item, bool value )
  628. {
  629. item.SetValue( IsUnderDragCursorProperty, value );
  630. }
  631. #endregion // IsUnderDragCursor
  632. }
  633. #endregion // ListBoxItemDragState
  634. #region ProcessDropEventArgs
  635. /// <summary>
  636. /// Event arguments used by the ListBoxDragDropManager.ProcessDrop event.
  637. /// </summary>
  638. /// <typeparam name="ItemType">The type of data object being dropped.</typeparam>
  639. public class ProcessDropEventArgs<ItemType> : EventArgs where ItemType : class
  640. {
  641. #region Data
  642. ObservableCollection<ItemType> itemsSource;
  643. ItemType dataItem;
  644. int oldIndex;
  645. int newIndex;
  646. DragDropEffects allowedEffects = DragDropEffects.None;
  647. DragDropEffects effects = DragDropEffects.None;
  648. #endregion // Data
  649. #region Constructor
  650. internal ProcessDropEventArgs(
  651. ObservableCollection<ItemType> itemsSource,
  652. ItemType dataItem,
  653. int oldIndex,
  654. int newIndex,
  655. DragDropEffects allowedEffects )
  656. {
  657. this.itemsSource = itemsSource;
  658. this.dataItem = dataItem;
  659. this.oldIndex = oldIndex;
  660. this.newIndex = newIndex;
  661. this.allowedEffects = allowedEffects;
  662. }
  663. #endregion // Constructor
  664. #region Public Properties
  665. /// <summary>
  666. /// The items source of the ListBox where the drop occurred.
  667. /// </summary>
  668. public ObservableCollection<ItemType> ItemsSource
  669. {
  670. get { return this.itemsSource; }
  671. }
  672. /// <summary>
  673. /// The data object which was dropped.
  674. /// </summary>
  675. public ItemType DataItem
  676. {
  677. get { return this.dataItem; }
  678. }
  679. /// <summary>
  680. /// The current index of the data item being dropped, in the ItemsSource collection.
  681. /// </summary>
  682. public int OldIndex
  683. {
  684. get { return this.oldIndex; }
  685. }
  686. /// <summary>
  687. /// The target index of the data item being dropped, in the ItemsSource collection.
  688. /// </summary>
  689. public int NewIndex
  690. {
  691. get { return this.newIndex; }
  692. }
  693. /// <summary>
  694. /// The drag drop effects allowed to be performed.
  695. /// </summary>
  696. public DragDropEffects AllowedEffects
  697. {
  698. get { return allowedEffects; }
  699. }
  700. /// <summary>
  701. /// The drag drop effect(s) performed on the dropped item.
  702. /// </summary>
  703. public DragDropEffects Effects
  704. {
  705. get { return effects; }
  706. set { effects = value; }
  707. }
  708. #endregion // Public Properties
  709. }
  710. #endregion // ProcessDropEventArgs
  711. }