WPF 4 拖放视觉元素作为光标
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3129443/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF 4 Drag and Drop with visual element as cursor
提问by Mark
I have a WPF 4 app which I want to enable drag and drop with, currently I have it working with a basic drag and drop implementation, but I have found that it would be much better if, instead of the mouse cursor changing over to represent the move operation, I could use an image underneath my finger.
我有一个 WPF 4 应用程序,我想用它来启用拖放,目前我有它使用基本的拖放实现,但我发现它会更好,而不是鼠标光标切换来表示移动操作,我可以使用手指下方的图像。
My drag and drop operation is initiated inside a custom user control, so I will need to insert a visual element into the visual tree and have it follow my finger around, perhaps I should enable the ManipulationDelta
event on my main window, check for a boolean then move the item around?
我的拖放操作是在自定义用户控件内启动的,因此我需要将一个可视元素插入到可视树中并让它跟随我的手指移动,也许我应该ManipulationDelta
在主窗口上启用该事件,然后检查布尔值移动项目?
回答by BrunoLM
From the mentioned article I was able to simplify a little. Basically what you need to do is subscribe in 3 events:
从提到的文章中,我能够稍微简化一下。基本上你需要做的是订阅 3 个事件:
- PreviewMouseLeftButtonDownEvent: The event that runs when you press the left button, you can start the drag action by invoking
DragDrop.DoDragDrop
- DropEvent: The event that runs when you drop something (control must have
AllowDrop
set totrue
in order to accept drops) - GiveFeedbackEvent: The event that runs all the time allowing you to give constant feedback
- PreviewMouseLeftButtonDownEvent:按下左键时运行的事件,可以通过调用开始拖动动作
DragDrop.DoDragDrop
- DropEvent:当你放下东西时运行的事件(控制必须
AllowDrop
设置true
为接受滴) - GiveFeedbackEvent:一直运行的事件,允许您提供持续的反馈
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
the first parameter is the element you are dragging, then the second is the data it is carrying and last the mouse effect.
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
第一个参数是你拖动的元素,第二个参数是它携带的数据,最后是鼠标效果。
This method locks the thread. So everything after its call will only execute when you stop dragging.
此方法锁定线程。因此,调用后的所有内容只会在您停止拖动时执行。
In the drop event you can retrieve the data you sent on the DoDragDrop
call.
在 drop 事件中,您可以检索您在DoDragDrop
通话中发送的数据。
The source for my tests are located bellow, and the result is:
我的测试源位于下面,结果是:
Full Source
完整来源
MainWindow.xaml
主窗口.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
主窗口.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;
};
}
}
Card.xaml.cs
卡片.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;
}
}
回答by Quartermeister
There is an example of using a custom drag cursor at Jaime Rodriguez msdn blog. You can handle the GiveFeedback
event and change the mouse cursor, but to use a custom Visual the author creates a new Window and updates the position on QueryContinueDrag
.
Jaime Rodriguez msdn 博客中有一个使用自定义拖动光标的示例。您可以处理该GiveFeedback
事件并更改鼠标光标,但要使用自定义 Visual,作者会创建一个新窗口并更新 上的位置QueryContinueDrag
。