Posterior a buscar en diferentes repositorios y páginas webs de internet al final nos encontramos con la resolución que te mostraremos ahora.
Solución:
Del artículo mencionado pude simplificar un poco. Básicamente, lo que debes hacer es suscribirte en 3 eventos:
- PreviewMouseLeftButtonDownEvent: El evento que se ejecuta cuando presiona el botón izquierdo, puede iniciar la acción de arrastre invocando
DragDrop.DoDragDrop
- DropEvent: El evento que se ejecuta cuando dejas caer algo (el control debe tener
AllowDrop
ajustado atrue
para aceptar gotas) - GiveFeedbackEvent: El evento que se ejecuta todo el tiempo, lo que le permite dar retroalimentación constante
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
el primer parámetro es el elemento que está arrastrando, luego el segundo son los datos que lleva y, por último, el efecto del mouse.
Este método bloquea el hilo. Por lo tanto, todo lo que esté después de su llamada solo se ejecutará cuando deje de arrastrar.
En caso de caída, puede recuperar los datos que envió en el DoDragDrop
llama.
La fuente de mis pruebas se encuentra a continuación, y el resultado es:
Muestra de arrastrar y soltar (gif)
Fuente completa
MainWindow.xaml
Card.xaml
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Shapes;
namespace TestWpfPure
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
public ObservableCollection Items get; set;
private readonly Style listStyle = null;
private Window _dragdropWindow = null;
public MainWindow()
InitializeComponent();
Items = new ObservableCollection(new List
new Card Text = "Task #01" ,
new Card Text = "Task #02" ,
new Card Text = "Task #03" ,
new Card Text = "Task #04" ,
new Card Text = "Task #05" ,
);
listStyle = new Style(typeof(ListBoxItem));
listStyle.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true));
listStyle.Setters.Add(new EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(CardList_PreviewMouseLeftButtonDown)));
listStyle.Setters.Add(new EventSetter(ListBoxItem.DropEvent, new DragEventHandler(CardList_Drop)));
listStyle.Setters.Add(new EventSetter(ListBoxItem.GiveFeedbackEvent, new GiveFeedbackEventHandler(CardList_GiveFeedback)));
CardListControl.ItemContainerStyle = listStyle;
DataContext = this;
protected void CardList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
if (sender is ListBoxItem)
var draggedItem = sender as ListBoxItem;
var card = draggedItem.DataContext as Card;
card.Effect = new DropShadowEffect
Color = new Color A = 50, R = 0, G = 0, B = 0 ,
Direction = 320,
ShadowDepth = 0,
Opacity = .75,
;
card.RenderTransform = new RotateTransform(2.0, 300, 200);
draggedItem.IsSelected = true;
// create the visual feedback drag and drop item
CreateDragDropWindow(card);
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
protected void CardList_Drop(object sender, DragEventArgs e)
var droppedData = e.Data.GetData(typeof(Card)) as Card;
var target = (sender as ListBoxItem).DataContext as Card;
int targetIndex = CardListControl.Items.IndexOf(target);
droppedData.Effect = null;
droppedData.RenderTransform = null;
Items.Remove(droppedData);
Items.Insert(targetIndex, droppedData);
// remove the visual feedback drag and drop item
if (this._dragdropWindow != null)
this._dragdropWindow.Close();
this._dragdropWindow = null;
private void CardList_GiveFeedback(object sender, GiveFeedbackEventArgs e)
// update the position of the visual feedback item
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
this._dragdropWindow.Left = w32Mouse.X;
this._dragdropWindow.Top = w32Mouse.Y;
private void CreateDragDropWindow(Visual dragElement)
this._dragdropWindow = new Window();
_dragdropWindow.WindowStyle = WindowStyle.None;
_dragdropWindow.AllowsTransparency = true;
_dragdropWindow.AllowDrop = false;
_dragdropWindow.Background = null;
_dragdropWindow.IsHitTestVisible = false;
_dragdropWindow.SizeToContent = SizeToContent.WidthAndHeight;
_dragdropWindow.Topmost = true;
_dragdropWindow.ShowInTaskbar = false;
Rectangle r = new Rectangle();
r.Width = ((FrameworkElement)dragElement).ActualWidth;
r.Height = ((FrameworkElement)dragElement).ActualHeight;
r.Fill = new VisualBrush(dragElement);
this._dragdropWindow.Content = r;
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
this._dragdropWindow.Left = w32Mouse.X;
this._dragdropWindow.Top = w32Mouse.Y;
this._dragdropWindow.Show();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
public Int32 X;
public Int32 Y;
;
Card.xaml.cs
using System.ComponentModel;
using System.Windows.Controls;
namespace TestWpfPure
///
/// Interaction logic for Card.xaml
///
public partial class Card : UserControl, INotifyPropertyChanged
private string text;
public string Text
get
return this.text;
set
this.text = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
public Card()
InitializeComponent();
DataContext = this;
public event PropertyChangedEventHandler PropertyChanged;
Hay un ejemplo del uso de un cursor de arrastre personalizado en el blog msdn de Jaime Rodríguez. Puedes manejar el GiveFeedback
evento y cambiar el cursor del mouse, pero para usar un Visual personalizado, el autor crea una nueva ventana y actualiza la posición en QueryContinueDrag
.