WPF 4 glisser-déposer avec l'élément visuel comme curseur

j'ai une application WPF 4 avec laquelle je veux pouvoir glisser-déposer, actuellement je l'ai avec une implémentation de base de glisser-déposer, mais j'ai trouvé qu'il serait beaucoup mieux si, au lieu de changer le curseur de la souris pour représenter l'opération de déplacement, je pouvais utiliser une image sous mon doigt.

Je ne semble pas trouver d'échantillons ou de doco qui ont ce travail hors de la boîte, donc je suppose que je vais devoir le construire sur mesure... des idées?

ma traînée et l'opération de largage est initiée à l'intérieur d'un contrôle d'utilisateur personnalisé, donc je vais avoir besoin d'insérer un élément visuel dans l'arbre visuel et le faire suivre de mon doigt autour, peut-être que je devrais activer le ManipulationDelta événement sur ma fenêtre principale, vérifiez pour un booléen alors déplacer l'élément autour?

Je ne fais que lancer des idées à ce stade, toute aide ou idées seraient grandement appréciées.

Cheers, Marque

7
demandé sur Mark 2010-06-28 04:20:06

2 réponses

il y a un exemple d'utilisation d'un curseur de drag personnalisé au Jaime Rodriguez blog msdn. Vous pouvez gérer l' GiveFeedback événement et changer le curseur de la souris, mais pour utiliser un visuel personnalisé l'auteur crée une nouvelle fenêtre et met à jour la position sur QueryContinueDrag.

9
répondu Quartermeister 2013-03-27 12:28:25

à partir de l'article mentionné, j'ai pu simplifier un peu. Fondamentalement, ce que vous devez faire est de vous inscrire en 3 événements:

  • PreviewMouseLeftButtonDownEvent: l'événement qui s'exécute lorsque vous appuyez sur le bouton gauche, vous pouvez lancer l'action de glisser en invoquant DragDrop.DoDragDrop
  • DropEvent L'événement qui s'exécute lorsque vous laissez tomber quelque chose (contrôle doit être AllowDroptrue pour accepter gouttes)
  • GiveFeedbackEvent L'événement qui fonctionne tout le temps, vous permettant de donner une rétroaction constante

DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move); le premier paramètre est l'élément que vous faites glisser, puis la seconde est que les données qu'il transporte et dernier de la souris effet.

cette méthode verrouille le thread. Donc tout après son appel ne s'exécute que lorsque vous arrêtez de traîner.

dans l'événement drop vous pouvez récupérer les données que vous avez envoyé sur le DoDragDrop appeler.

La source pour mes tests sont situés ci-dessous, et le résultat est:

Exemple de drag n' drop (gif)

Source

MainWindow.xaml

<Window x:Class="TestWpfPure.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:uc="clr-namespace:TestWpfPure"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox x:Name="CardListControl" AllowDrop="True" ItemsSource="{Binding Items}" />
    </Grid>
</Window>

Carte.xaml

<UserControl x:Class="TestWpfPure.Card"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Border x:Name="CardBorder" BorderBrush="Black" BorderThickness="3" HorizontalAlignment="Left" Height="40" VerticalAlignment="Top" Width="246" RenderTransformOrigin="0.5,0.5" CornerRadius="6">
            <TextBlock Text="{Binding Text}" TextWrapping="Wrap" FontFamily="Arial" FontSize="14" />
        </Border>
    </Grid>
</UserControl>

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
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<Card> Items { get; set; }

        private readonly Style listStyle = null;
        private Window _dragdropWindow = null;

        public MainWindow()
        {
            InitializeComponent();

            Items = new ObservableCollection<Card>(new List<Card>
            {
                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;
        };
    }
}

Carte.XAML.cs

using System.ComponentModel;
using System.Windows.Controls;

namespace TestWpfPure
{
    /// <summary>
    /// Interaction logic for Card.xaml
    /// </summary>
    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;
    }
}
20
répondu BrunoLM 2015-01-15 23:45:54