[WPF] ListView mit Maus ordnen (Behavior)

    • C#
    • .NET (FX) 4.0

    Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      [WPF] ListView mit Maus ordnen (Behavior)

      Moin,

      da ich es selbst für ein Projekt brauchte, es aber anscheinend nix fertiges gibt, hab ich mal ein Behavior geschrieben, dass es erlaubt, ein WPF ListView mit der Maus neu zu ordnen.
      Das ganze funktioniert ab .NET 4.0 da, zumindest hab ich's nicht gefunden, die Notwendigen Assembleys nicht für 3.5 verfügbar sind.

      Der Code für das Behavior ist:
      Spoiler anzeigen

      C#-Quellcode

      1. public class MouseOrganizeBehavior : Behavior<ListView>
      2. {
      3. protected static DependencyProperty isScrollEnabledProperty = DependencyProperty.Register("isScrollEnabled", typeof(bool), typeof(MouseOrganizeBehavior), new PropertyMetadata(true));
      4. public bool isScrollEnabled
      5. {
      6. get { return (bool)GetValue(isScrollEnabledProperty); }
      7. set { SetValue(isScrollEnabledProperty, value); }
      8. }
      9. public double UpperBound { get; set; }
      10. public double LowerBound { get; set; }
      11. private string dataType;
      12. public MouseOrganizeBehavior()
      13. {
      14. UpperBound = 10;
      15. LowerBound = 20;
      16. }
      17. protected override void OnAttached()
      18. {
      19. AssociatedObject.AllowDrop = true;
      20. dataType = String.Format("{0} - {1}", "ListViewOrderData", Guid.NewGuid().ToString());
      21. AssociatedObject.MouseMove += (s, e) =>
      22. {
      23. if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
      24. {
      25. if (AssociatedObject.SelectedItem == null) return;
      26. DataObject d = new DataObject(dataType, AssociatedObject.SelectedItem);
      27. DragDrop.DoDragDrop(AssociatedObject, d, DragDropEffects.Move | DragDropEffects.Scroll);
      28. }
      29. };
      30. AssociatedObject.DragOver += (s, e) =>
      31. {
      32. e.Effects = DragDropEffects.None;
      33. if (e.Data.GetFormats().Contains(dataType))
      34. {
      35. var index = GetCurrentIndex(e.GetPosition);
      36. MoveObject(DataToObj(e.Data), index);
      37. ScrollList(index, e.GetPosition(AssociatedObject).Y);
      38. e.Effects = DragDropEffects.Move | DragDropEffects.Scroll;
      39. }
      40. e.Handled = true;
      41. };
      42. AssociatedObject.Drop += (s, e) =>
      43. {
      44. if (!e.Data.GetDataPresent(dataType)) return;
      45. object data = e.Data.GetData(dataType);
      46. var orgIndex = AssociatedObject.SelectedIndex;
      47. var index = GetCurrentIndex(e.GetPosition);
      48. if (index < 0) return;
      49. if (!MoveObject(data, index)) throw new Exception("Type not sortable!");
      50. e.Handled = true;
      51. };
      52. }
      53. protected override void OnDetaching()
      54. {
      55. AssociatedObject.AllowDrop = false;
      56. base.OnDetaching();
      57. }
      58. protected bool MoveObject(object obj, int targetIndex)
      59. {
      60. IList list = AssociatedObject.ItemsSource as IList ?? AssociatedObject.Items as IList;
      61. if (list == null) return false;
      62. if (targetIndex < 0) return false;
      63. list.Remove(obj);
      64. list.Insert(targetIndex, obj);
      65. return true;
      66. }
      67. protected void ScrollList(int targetIndex, double mouseY)
      68. {
      69. if (!isScrollEnabled || targetIndex < 0) return;
      70. if (mouseY < UpperBound)
      71. {
      72. if (targetIndex > 0)
      73. MoveListboxToIndex(targetIndex - 1);
      74. }
      75. else if (mouseY > AssociatedObject.ActualHeight - LowerBound)
      76. if (targetIndex <= AssociatedObject.Items.Count + 1 && !(targetIndex + 1 == AssociatedObject.Items.Count))
      77. MoveListboxToIndex(targetIndex + 1);
      78. }
      79. protected void MoveListboxToIndex(int index)
      80. {
      81. AssociatedObject.ScrollIntoView(AssociatedObject.Items[index]);
      82. }
      83. protected object DataToObj(IDataObject obj)
      84. {
      85. return obj.GetDataPresent(dataType) ? obj.GetData(dataType) : null;
      86. }
      87. protected int GetCurrentIndex(GetPositionDelegate getPosition)
      88. {
      89. int index = -1;
      90. for (int i = 0; i < AssociatedObject.Items.Count; ++i)
      91. {
      92. ListViewItem item = GetListViewItem(i);
      93. if (this.IsMouseOverTarget(item, getPosition))
      94. {
      95. index = i;
      96. break;
      97. }
      98. }
      99. return index;
      100. }
      101. protected bool IsMouseOverTarget(Visual target, GetPositionDelegate getPosition)
      102. {
      103. if (target == null) return false;
      104. Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
      105. Point mousePos = getPosition((IInputElement)target);
      106. return bounds.Contains(mousePos);
      107. }
      108. protected delegate Point GetPositionDelegate(IInputElement element);
      109. protected ListViewItem GetListViewItem(int index)
      110. {
      111. if (AssociatedObject.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
      112. return null;
      113. return AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
      114. }
      115. }


      Wichtig: Die Basisklasse Behavior<T> findet man im Assembly System.Windows.Interactivity. Hinzufügen nicht vergessen!
      (Den Code, um den Index des Eintrages zu bestimmen über den die Maus steht, hab ich im Internet gefunden, aber leider vergessen, mir die Quelle zu merken. Der Teil stammt also nicht von mir!!)

      Das ganze wird dann so auf ein ListView angewendet:

      Spoiler anzeigen

      XML-Quellcode

      1. <ListView>
      2. <i:Interaction.Behaviors>
      3. <Behaviors:MouseOrganizeBehavior />
      4. </i:Interaction.Behaviors>
      5. <System:Int32>0</System:Int32>
      6. <System:Int32>1</System:Int32>
      7. <System:Int32>2</System:Int32>
      8. <System:Int32>3</System:Int32>
      9. <System:Int32>4</System:Int32>
      10. <System:Int32>5</System:Int32>
      11. <System:Int32>6</System:Int32>
      12. <System:Int32>7</System:Int32>
      13. <System:Int32>8</System:Int32>
      14. <System:Int32>9</System:Int32>
      15. <System:Int32>10</System:Int32>
      16. </ListView>


      Auch hier ist wichtig den XML-Namespace zu setzen. Also oben im <Window> xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" hinzufügen und den Namespace in dem die Behavior-Klasse liegt.
      Natürlich funktioniert das Sortieren auch mit gebundenen Datenquellen. Hierbei wird auch, soweit möglich, die Quelle selber mit sortiert. Sie muss bloß IList implementieren. Die ist aber zum Beispiel bei der ObservableCollection<T> der Fall.
      Die Scrollfunktionalität ist zwar vorhanden, hat aber noch Potentential. Für meine Anwendung reicht es völlig.

      Viel Spaß damit und hoffentlich kann ich so irgendwem das gleiche Problem wie ich es hatte ersparen.

      Zakath