我有一个WPF 4应用程序,希望启用拖放功能。目前,我已经通过基本的拖放实现使其工作正常,但是我发现如果可以使用手指下面的图像代替鼠标光标来表示移动操作,那将会更好。
我的拖放操作是在自定义用户控件内启动的,因此我需要在可视树中插入一个视觉元素,并使其跟随我的手指移动,也许我应该在主窗口上启用ManipulationDelta
事件,检查布尔值然后移动物品?
我有一个WPF 4应用程序,希望启用拖放功能。目前,我已经通过基本的拖放实现使其工作正常,但是我发现如果可以使用手指下面的图像代替鼠标光标来表示移动操作,那将会更好。
我的拖放操作是在自定义用户控件内启动的,因此我需要在可视树中插入一个视觉元素,并使其跟随我的手指移动,也许我应该在主窗口上启用ManipulationDelta
事件,检查布尔值然后移动物品?
在提及的文章中,我能够简化一些内容。基本上,您需要订阅三个事件:
DragDrop.DoDragDrop
开始拖动操作 AllowDrop
设置为 true
,才能接受拖放) DragDrop.DoDragDrop(draggedItem,draggedItem.DataContext,DragDropEffects.Move);
第一个参数是您正在拖动的元素,然后是它所携带的数据,最后是鼠标效果。
此方法会锁定线程。因此,在其调用之后的所有内容都将在停止拖动时执行。
在放置事件中,您可以检索发送到 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
->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;
};
}
}
Card.xaml.cs
:该文件是一个名为“Card”的自定义控件在C#语言下的实现。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;
}
}
这里有一个使用自定义拖动光标的示例在Jaime Rodriguez msdn博客上。你可以处理GiveFeedback
事件并更改鼠标光标,但要使用自定义Visual,作者创建了一个新窗口,并在QueryContinueDrag
上更新位置。
System.Windows.Input.Cursor
,而我不知道如何使用 UIElement...(我想要有动画和更多)。谢谢你的帮助。不过我正在考虑使用这个库的自定义版本:http://dotnetslackers.com/ado_net/re-191632_generic_wpf_drag_and_drop_adorner.aspx - Mark
DoDragDrop
之后调用窗口的Close
方法,这样无论拖放在哪里结束,窗口都会关闭。 - BradleyDotNET