2010-07-28 10 views
44

Estoy tratando de encontrar la forma de mover los elementos en un cuadro de lista previamente poblado hacia arriba y hacia abajo a través de arrastrar el mouse.WPF C#: Reorganizar elementos en el cuadro de lista arrastrando y soltando

He mirado el método Control.DoDragDrop desde la API de microsoft, pero todavía no puedo hacer que haga nada.

Agradecería cualquier ayuda ya que soy nuevo en el entorno de los estudios visuales.

Respuesta

58

He intentado crear una con ObservableCollection, echar un vistazo

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

proceso de arrastrar y soltar

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

Nota:

  • una cosa chupa en la ejecución es ya utiliza el evento PreviewMouseLeftButtonDown, el elemento arrastrado parece no seleccionado
  • y también para una implementación más fácil el destino de colocación es los elementos de cuadro de lista y no el cuadro de lista en sí - puede ser que necesite una solución mejor para este código
+19

Si cambia PreviewMouseLeftButtonDown a PreviewMouseMoveEvent, y luego añadir e.LeftButton == MouseButtonState.Pressed a su declaración if, arregla el problema de selección. – Charles

+1

Necesitaba agregar 'listbox1.Items.Refresh();' al final del controlador Drop, pero aparte de eso, ¡funciona genial! – Andrew

+0

@Andrew, ¿usas observablecollection? si lo usa, debe actualizar automáticamente – dnr3

1

reparación:

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

Usando las respuestas de dnr3 que tengo versión creada con problemas de selección fijos.

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

Versión con soporte para genéricos y enlace de datos.

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

¡Funciona muy bien, gracias! – IdanB

+0

Esto es muy agradable, gracias a ti y a @ dnr3. Lo único que podría ver que podría mejorar es si el cursor, cuando se arrastra, podría desplazarse sobre la parte del cuadro de lista que no tiene archivos. Tal como está, el cursor cambia a un conjunto nulo, sin arrastrar el símbolo si arrastra demasiado bajo. Y supongo que si pudieras arrastrar varios elementos. No es que me queje, esto es más que suficiente para comenzar. – premes

4

se recomienda usar el arrastrar y soltar el comportamiento llamados GongSolutions.WPF.DragDrop. Permite el uso de casos de uso de estilo MVVM mediante el establecimiento de propiedades adjuntas para habilitarlo, sin necesidad de código en sus vistas. Debería consultar el enlace para ver un ejemplo simple.

2

Esto me ha ayudado muchísimo gracias. Especialmente la versión de genéricos.

me hicieron las siguientes modificaciones:

Debido a que no se establece la DataContext del cuadro de lista (sólo el ItemsSource), utilizo

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

en el método Move.

Y al final del "movimiento" Yo añadido:

this.SelectedItem = source; 

como quiera que el usuario tenga el elemento movido como la selección actual.

0

Tomé la respuesta de dnr3 y lo alteró para su aplicación en XAML. El mismo resultado, simplemente prefiero hacer lo que pueda en XAML en lugar de hacerlo en el código subyacente.

En lugar del código subyacente:

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; 

poner esto en el 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> 
Cuestiones relacionadas