WPF 4以可视元素作为光标进行拖放

WPF 4以可视元素作为光标进行拖放,wpf,drag-and-drop,.net-4.0,Wpf,Drag And Drop,.net 4.0,我有一个WPF 4应用程序,我想启用拖放功能,目前我有一个基本的拖放实现,但我发现如果我可以在手指下使用一个图像,而不是鼠标光标切换来表示移动操作,那会更好 我的拖放操作是在自定义用户控件中启动的,因此我需要在可视化树中插入一个可视化元素,并让它跟随我的手指,也许我应该在我的主窗口上启用操纵delta事件,检查布尔值,然后移动项目?有一个使用自定义拖动光标的示例。您可以处理该事件并更改鼠标光标,但要使用自定义的视觉效果,作者会创建一个新窗口并更新其位置。从前面提到的文章中,我可以简化一点。基本上

我有一个WPF 4应用程序,我想启用拖放功能,目前我有一个基本的拖放实现,但我发现如果我可以在手指下使用一个图像,而不是鼠标光标切换来表示移动操作,那会更好


我的拖放操作是在自定义用户控件中启动的,因此我需要在可视化树中插入一个可视化元素,并让它跟随我的手指,也许我应该在我的主窗口上启用
操纵delta
事件,检查布尔值,然后移动项目?

有一个使用自定义拖动光标的示例。您可以处理该事件并更改鼠标光标,但要使用自定义的视觉效果,作者会创建一个新窗口并更新其位置。

从前面提到的文章中,我可以简化一点。基本上,您需要做的是订阅3个活动:

  • PreviewMouseLeftButtonDownEvent:当您按下左按钮时运行的事件,您可以通过调用
    DragDrop.DoDragDrop来启动拖动操作
  • DropEvent:当您丢弃某些内容时运行的事件(控件必须将
    AllowDrop
    设置为
    true
    ,才能接受丢弃)
  • GiveFeedbackEvent:始终运行的事件,允许您不断提供反馈
DragDrop.DoDragDrop(draggedItem,draggedItem.DataContext,DragDropEffects.Move)第一个参数是您正在拖动的元素,第二个参数是它所携带的数据,最后是鼠标效果

此方法锁定线程。因此,调用后的所有内容都将仅在您停止拖动时执行

在drop事件中,您可以检索在
DoDragDrop
调用中发送的数据

我的测试来源如下,结果如下:

全源 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>

Card.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;
        };
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.Runtime.InteropServices;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Input;
使用System.Windows.Media;
使用System.Windows.Media.Effects;
使用System.Windows.Shapes;
命名空间TestWpfPure
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
公共ObservableCollection项{get;set;}
私有只读样式listStyle=null;
私有窗口_dragdropWindow=null;
公共主窗口()
{
初始化组件();
Items=新的ObservableCollection(新列表
{
新卡片{Text=“Task#01”},
新卡片{Text=“Task#02”},
新卡片{Text=“Task#03”},
新卡片{Text=“Task#04”},
新卡片{Text=“Task#05”},
});
listStyle=新样式(typeof(ListBoxItem));
添加(新的Setter(ListBoxItem.AllowDropProperty,true));
添加(新事件设置器(ListBoxItem.PreviewMouseLeftButtonDownEvent,新鼠标按钮VentHandler(CardList_PreviewMouseLeftButtonDown));
添加(新的事件设置器(ListBoxItem.DropEvent,新的DragEventHandler(CardList_-Drop));
添加(新的事件设置器(ListBoxItem.GiveFeedbackEvent,新的GiveFeedbackEventHandler(CardList_GiveFeedback));
CardListControl.ItemContainerStyle=listStyle;
DataContext=this;
}
受保护的无效卡片列表\u预览鼠标左键向下(对象发送器,鼠标按钮ventargs e)
{
如果(发件人是ListBoxItem)
{
var draggedItem=作为ListBoxItem的发件人;
var card=draggedItem.DataContext作为卡片;
card.Effect=新的DropShadow效果
{
颜色=新颜色{A=50,R=0,G=0,B=0},
方向=320,
阴影深度=0,
不透明度=.75,
};
card.RenderTransform=新的旋转变换(2.0300200);
draggedItem.IsSelected=true;
//创建视觉反馈拖放项
创建DragDropWindow(卡片);
DragDrop.DoDragDrop(draggedItem、draggedItem.DataContext、DragDropEffects.Move);
}
}
受保护的无效卡片列表(对象发送方、DragEventArgs e)
{
var droppedData=e.Data.GetData(卡片类型))作为卡片;
var target=(发送方作为ListBoxItem)。DataContext作为卡;
int targetIndex=CardListControl.Items.IndexOf(目标);
droppedData.Effect=null;
droppedData.RenderTransform=null;
项目。删除(删除数据);
项目。插入(targetIndex、droppedData);
//删除视觉反馈拖放项
如果(此._dragdropWindow!=null)
{
这是._dragdropWindow.Close();
此参数为.\u dragdropWindow=null;
}
}
私有作废卡片列表\u GiveFeedback(对象发送者,GiveFeedbackEventArgs e)
{
//更新视觉反馈项的位置
Win32Point w32Mouse=新的Win32Point();
GetCursorPos(参考w32Mouse);
这个._dragdropWindow.Left=w32Mouse.X;
这个._dragdropWindow.Top=w32Mouse.Y;
}
专用void CreateDragDropWindow(可视dragElement)
{
这是._dragdropWindow=新窗口();
_dragdropWindow.WindowsStyle=WindowsStyle.None;
_dragdropWindow.AllowTransparency=true;
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;
    }
}