2010-07-28 98 views
44

我想知道如何通過鼠標拖動來上下移動預先填充的列表框中的項目。WPF C#:通過拖放來重新排列列表框中的項目

我已經看過來自Microsoft的api的Control.DoDragDrop方法,但我仍然無法讓它做任何事情。

我很感激任何幫助,因爲我是視覺工作室環境的新手。

回答

58

我已經嘗試過創建使用的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

1

修復代碼更好的解決方案:

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

使用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

4

我會建議使用拖放行爲稱爲GongSolutions.WPF.DragDrop。它允許MVVM風格的用例使用附加的屬性設置器來啓用它,在視圖中不需要代碼。你應該查看一個簡單的例子的鏈接。

2

這對我非常感謝。特別是泛型版本。

我提出了以下修改:

因爲我沒有設置ListBox的DataContext的(只是的ItemsSource),我在Move方法使用

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

而在「移動」到最後我說:

this.SelectedItem = source; 

,我希望用戶有移動的項目作爲當前的選擇。

0

我拿出了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>