WPF C#:通过拖放来重新排列列表框中的项目

我想弄清楚如何通过鼠标拖动上下移动预填充列表框中的项目。

我已经从微软的api中看到了Control.DoDragDrop方法,但是我仍然无法做任何事情。

我会很感激任何帮助,因为我是新的视觉工作室环境。

我试着创build一个使用observablecollection,看看

ObservableCollection<Emp> _empList = new ObservableCollection<Emp>(); public Window1() { InitializeComponent(); _empList .Add(new Emp("1", 22)); _empList .Add(new Emp("2", 18)); _empList .Add(new Emp("3", 29)); _empList .Add(new Emp("4", 9)); _empList .Add(new Emp("5", 29)); _empList .Add(new Emp("6", 9)); listbox1.DisplayMemberPath = "Name"; listbox1.ItemsSource = _empList; Style itemContainerStyle = new Style(typeof(ListBoxItem)); itemContainerStyle.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true)); itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(s_PreviewMouseLeftButtonDown))); itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.DropEvent, new DragEventHandler(listbox1_Drop))); listbox1.ItemContainerStyle = itemContainerStyle; } 

拖放过程

  void s_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (sender is ListBoxItem) { ListBoxItem draggedItem = sender as ListBoxItem; DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move); draggedItem.IsSelected = true; } } void listbox1_Drop(object sender, DragEventArgs e) { Emp droppedData = e.Data.GetData(typeof(Emp)) as Emp; Emp target = ((ListBoxItem)(sender)).DataContext as Emp; int removedIdx = listbox1.Items.IndexOf(droppedData); int targetIdx = listbox1.Items.IndexOf(target); if (removedIdx < targetIdx) { _empList.Insert(targetIdx + 1, droppedData); _empList.RemoveAt(removedIdx); } else { int remIdx = removedIdx+1; if (_empList.Count + 1 > remIdx) { _empList.Insert(targetIdx, droppedData); _empList.RemoveAt(remIdx); } } } 

注意:

  • 有一件事在实现中很糟糕,因为它使用了PreviewMouseLeftButtonDown事件,拖动的项目看起来没有被选中
  • 而且为了更容易的实现,放置目标是列表框项目而不是列表框本身 – 可能需要更好的解决scheme

使用dnr3的答案,我已经创build了固定select问题的版本。

Window1.xaml

 <Window x:Class="ListBoxReorderDemo.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="ListBoxReorderDemo" Height="300" Width="300" WindowStartupLocation="CenterScreen"> <Grid> <ListBox x:Name="listBox"/> </Grid> </Window> 

Window1.xaml.cs

 using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; namespace ListBoxReorderDemo { public class Item { public string Name { get; set; } public Item(string name) { this.Name = name; } } public partial class Window1 : Window { private Point _dragStartPoint; private T FindVisualParent<T>(DependencyObject child) where T : DependencyObject { var parentObject = VisualTreeHelper.GetParent(child); if (parentObject == null) return null; T parent = parentObject as T; if (parent != null) return parent; return FindVisualParent<T>(parentObject); } private IList<Item> _items = new ObservableCollection<Item>(); public Window1() { InitializeComponent(); _items.Add(new Item("1")); _items.Add(new Item("2")); _items.Add(new Item("3")); _items.Add(new Item("4")); _items.Add(new Item("5")); _items.Add(new Item("6")); listBox.DisplayMemberPath = "Name"; listBox.ItemsSource = _items; listBox.PreviewMouseMove += ListBox_PreviewMouseMove; var style = new Style(typeof(ListBoxItem)); style.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true)); style.Setters.Add( new EventSetter( ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(ListBoxItem_PreviewMouseLeftButtonDown))); style.Setters.Add( new EventSetter( ListBoxItem.DropEvent, new DragEventHandler(ListBoxItem_Drop))); listBox.ItemContainerStyle = style; } private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e) { Point point = e.GetPosition(null); Vector diff = _dragStartPoint - point; if (e.LeftButton == MouseButtonState.Pressed && (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance || Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)) { var lb = sender as ListBox; var lbi = FindVisualParent<ListBoxItem>(((DependencyObject)e.OriginalSource)); if (lbi != null) { DragDrop.DoDragDrop(lbi, lbi.DataContext, DragDropEffects.Move); } } } private void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { _dragStartPoint = e.GetPosition(null); } private void ListBoxItem_Drop(object sender, DragEventArgs e) { if (sender is ListBoxItem) { var source = e.Data.GetData(typeof(Item)) as Item; var target = ((ListBoxItem)(sender)).DataContext as Item; int sourceIndex = listBox.Items.IndexOf(source); int targetIndex = listBox.Items.IndexOf(target); Move(source, sourceIndex, targetIndex); } } private void Move(Item source, int sourceIndex, int targetIndex) { if (sourceIndex < targetIndex) { _items.Insert(targetIndex + 1, source); _items.RemoveAt(sourceIndex); } else { int removeIndex = sourceIndex + 1; if (_items.Count + 1 > removeIndex) { _items.Insert(targetIndex, source); _items.RemoveAt(removeIndex); } } } } } 

支持generics和数据绑定的版本。

Window1.xaml

 <Window x:Class="ListBoxReorderDemo.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ListBoxReorderDemo" Title="ListBoxReorderDemo" Height="300" Width="300" WindowStartupLocation="CenterScreen"> <Grid> <local:ItemDragAndDropListBox x:Name="listBox" ItemsSource="{Binding}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}"/> </DataTemplate> </ListBox.ItemTemplate> </local:ItemDragAndDropListBox> </Grid> </Window> 

Window1.xaml.cs

 using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; namespace ListBoxReorderDemo { public class DragAndDropListBox<T> : ListBox where T : class { private Point _dragStartPoint; private P FindVisualParent<P>(DependencyObject child) where P : DependencyObject { var parentObject = VisualTreeHelper.GetParent(child); if (parentObject == null) return null; P parent = parentObject as P; if (parent != null) return parent; return FindVisualParent<P>(parentObject); } public DragAndDropListBox() { this.PreviewMouseMove += ListBox_PreviewMouseMove; var style = new Style(typeof(ListBoxItem)); style.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true)); style.Setters.Add( new EventSetter( ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(ListBoxItem_PreviewMouseLeftButtonDown))); style.Setters.Add( new EventSetter( ListBoxItem.DropEvent, new DragEventHandler(ListBoxItem_Drop))); this.ItemContainerStyle = style; } private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e) { Point point = e.GetPosition(null); Vector diff = _dragStartPoint - point; if (e.LeftButton == MouseButtonState.Pressed && (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance || Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)) { var lb = sender as ListBox; var lbi = FindVisualParent<ListBoxItem>(((DependencyObject)e.OriginalSource)); if (lbi != null) { DragDrop.DoDragDrop(lbi, lbi.DataContext, DragDropEffects.Move); } } } private void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { _dragStartPoint = e.GetPosition(null); } private void ListBoxItem_Drop(object sender, DragEventArgs e) { if (sender is ListBoxItem) { var source = e.Data.GetData(typeof(T)) as T; var target = ((ListBoxItem)(sender)).DataContext as T; int sourceIndex = this.Items.IndexOf(source); int targetIndex = this.Items.IndexOf(target); Move(source, sourceIndex, targetIndex); } } private void Move(T source, int sourceIndex, int targetIndex) { if (sourceIndex < targetIndex) { var items = this.DataContext as IList<T>; if (items != null) { items.Insert(targetIndex + 1, source); items.RemoveAt(sourceIndex); } } else { var items = this.DataContext as IList<T>; if (items != null) { int removeIndex = sourceIndex + 1; if (items.Count + 1 > removeIndex) { items.Insert(targetIndex, source); items.RemoveAt(removeIndex); } } } } } public class Item { public string Name { get; set; } public Item(string name) { this.Name = name; } } public class ItemDragAndDropListBox : DragAndDropListBox<Item> { } public partial class Window1 : Window { private IList<Item> _items = new ObservableCollection<Item>(); public Window1() { InitializeComponent(); _items.Add(new Item("1")); _items.Add(new Item("2")); _items.Add(new Item("3")); _items.Add(new Item("4")); _items.Add(new Item("5")); _items.Add(new Item("6")); listBox.DataContext = _items; } } } 

我build议使用称为GongSolutions.WPF.DragDrop的拖放行为。 它允许MVVM风格的用例使用附加的属性设置器来启用它,而不需要在视图中隐藏代码。 你应该检查一个简单的例子的链接。

维修代码:

 private void listbox1_Drop(object sender, DragEventArgs e) { if (sender is ListBoxItem) { Emp droppedData = e.Data.GetData(typeof(Emp)) as Emp; Emp target = ((ListBoxItem)(sender)).DataContext as Emp; int removedIdx = listbox1.Items.IndexOf(droppedData); int targetIdx = listbox1.Items.IndexOf(target); if (removedIdx < targetIdx) { _empList.Insert(targetIdx + 1, droppedData); _empList.RemoveAt(removedIdx); } else { int remIdx = removedIdx + 1; if (_empList.Count + 1 > remIdx) { _empList.Insert(targetIdx, droppedData); _empList.RemoveAt(remIdx); } } } } 

这让我非常感谢你。 特别是generics版本。

我做了以下修改:

因为我没有设置ListBox的DataContext(只是ItemsSource),所以我使用

 var items = this.ItemsSource as IList<T>; 

在Move方法中。

而在“移动”结束时,我补充道:

 this.SelectedItem = source; 

因为我希望用户有移动的项目作为当前的select。