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

问题描述:

我想知道如何通过鼠标拖动来上下移动预先填充的列表框中的项目。WPF C#:通过拖放来重新排列列表框中的项目

我已经看过来自Microsoft的api的Control.DoDragDrop方法,但我仍然无法让它做任何事情。

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

我已经尝试过创建使用的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事件,拖动的项目看起来像没有选中
  • ,也为更简单的实现放置目标是列表框中的项目,而不是列表框本身 - 可能需要为这个
+19

如果更改的PreviewMouseLeftButtonDown到PreviewMouseMoveEvent,然后添加e.LeftButton == MouseButtonState.Pressed到:

Style itemContainerStyle = new Style(typeof(ListBoxItem)); itemContainerStyle.Setters.Add(new Setter(AllowDropProperty, true)); itemContainerStyle.Setters.Add(new EventSetter(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(s_PreviewMouseLeftButtonDown))); itemContainerStyle.Setters.Add(new EventSetter(DropEvent, new DragEventHandler(listbox1_Drop))); listbox1.ItemContainerStyle = itemContainerStyle; 

在XAML将这个你的if语句,你解决了选择问题。 – Charles 2013-04-24 19:27:18

+1

我需要将'listbox1.Items.Refresh();'添加到Drop处理程序的末尾,但除此之外,它效果很好! – Andrew 2015-10-22 19:18:30

+0

@Andrew,你使用observablecollection吗?如果你使用它,它应该是自动更新 – dnr3 2015-10-23 02:37:48

修复代码更好的解决方案:

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); 
      } 
     } 
    } 
} 

使用dnr3的答案我有创建具有固定选择问题的版本。

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); 
       } 
      } 
     } 
    } 
} 

版本与仿制药和数据绑定的支持。

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; 
     } 
    } 
} 
+0

很好,谢谢! – IdanB 2015-09-19 17:44:54

+0

这真的很好,谢谢你和@ dnr3。我能看到的唯一可以改进的地方是,如果光标在拖动时可以悬停在没有文件的列表框部分。实际上,如果拖动过低,则光标变为空集,不拖动符号。我想如果你可以拖动多个项目。不是我在抱怨,这已经足够开始了。 – premes 2015-11-09 03:21:39

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

这对我非常感谢。特别是泛型版本。

我提出了以下修改:

因为我没有设置ListBox的DataContext的(只是的ItemsSource),我在Move方法使用

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

而在“移动”到最后我说:

this.SelectedItem = source; 

,我希望用户有移动的项目作为当前的选择。

我拿出了dnr3的答案并修改了它在XAML中的实现。同样的结果,只是喜欢在XAML中做我能做的事情,而不是在代码隐藏方面。

代替的后台代码:

<Window.Resources> 
    <Style x:Key="ListBoxDragDrop" TargetType="{x:Type ListBoxItem}"> 
     <Setter Property="AllowDrop" Value="true"/> 
     <EventSetter Event="PreviewMouseMove" Handler="s_PreviewMouseMoveEvent"/> 
     <EventSetter Event="Drop" Handler="listbox1_Drop"/> 
    </Style> 
</Window.Resources> 
<Grid> 
    <ListBox x:Name="listbox1" ItemContainerStyle="{StaticResource ListBoxDragDrop}" HorizontalAlignment="Left" Height="299" Margin="10,10,0,0" VerticalAlignment="Top" Width="224"/> 
</Grid>