Saltar al contenido

WPF 4 Arrastrar y soltar con elemento visual como cursor

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 a true 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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *