上一篇介紹了使用事件為主的 Drag and Drop 做法,有了基本的知識後,這一篇要利用附加屬性來製作一個 Drag and Drop Canvas。
先簡單規劃基本的 Drag and Drop 會有哪些屬性的需求:
(1) 需要複製的資料型態
(2) 拖曳來源
(3) 拖曳目標
先建立一個類別繼承 Canvas
public class DragAndDropCanvas : Canvas
建立 DropDataFormat 附加屬性,設定拖曳時複製的資料型態。
public static readonly DependencyProperty DropDataFormatProperty
= DependencyProperty.RegisterAttached("DropDataFormat", typeof(string), typeof(DragAndDropCanvas), new FrameworkPropertyMetadata("Text"));
public static void SetDropDataFormat(UIElement element, string value)
{
element.SetValue(DropDataFormatProperty, value);
}
public static string GetDropDataFormat(UIElement element)
{
return (string)element.GetValue(DropDataFormatProperty);
}
建立 DragSource 附加屬性,設定拖曳來源是使用哪個 DependencyProperty。
public static readonly DependencyProperty DragSourceProperty
= DependencyProperty.RegisterAttached("DragSource", typeof(DependencyProperty), typeof(DragAndDropCanvas), new FrameworkPropertyMetadata(null));
public static void SetDragSource(UIElement element, DependencyProperty value)
{
element.SetValue(DragSourceProperty, value);
}
public static DependencyProperty GetDragSource(UIElement element)
{
return (DependencyProperty)element.GetValue(DragSourceProperty);
}
建立 DropTarget 附加屬性,設定拖曳目標會使用哪個 DependencyProperty。
public static readonly DependencyProperty DropTargetProperty
= DependencyProperty.RegisterAttached("DropTarget", typeof(DependencyProperty), typeof(DragAndDropCanvas), new FrameworkPropertyMetadata(null));
public static void SetDropTarget(UIElement element, DependencyProperty value)
{
element.SetValue(DropTargetProperty, value);
}
public static DependencyProperty GetDropTarget(UIElement element)
{
return (DependencyProperty)element.GetValue(DropTargetProperty);
}
接下來的技巧和上一篇差不多,但有幾點需要注意,第一個是為了避免麻煩,可以在建構式先將 AllowDrop 設定為 true;第二件事情是由於 WPF 路由事件的設計,這邊要使用 PreviewMouseDown 而非 MouseDown,而且不使用 Canvas 的 PreviewMouseDown 事件而是採用覆寫 OnPreviewMouseDown 的方式。
根據以上的敘述,加入必要的程式碼。
public DragAndDropCanvas()
{
AllowDrop = true;
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
if (e.Source is UIElement element)
{
var property = DragAndDropCanvas.GetDragSource(element);
if (property == null) return;
var data = element.GetValue(property);
DragDrop.DoDragDrop(element, data, DragDropEffects.Copy);
}
}
protected override void OnDrop(DragEventArgs e)
{
if (e.Source is UIElement element)
{
var property = DragAndDropCanvas.GetDropTarget(element);
if (property == null) return;
var format = GetDropDataFormat(element);
if (format == null) return;
if (e.Data.GetDataPresent(format))
{
element.SetValue(property, e.Data.GetData(format));
}
}
}
最後,來寫一段簡單的 xaml 測試一下前述的 DragAndDropCanvas。
<Window x:Class="DragDropWPFSample003.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DragDropWPFSample003"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<local:DragAndDropCanvas>
<Button Content="CopyThisText" Canvas.Top="10" Canvas.Left="10"
local:DragAndDropCanvas.DropDataFormat="{x:Static DataFormats.Text}"
local:DragAndDropCanvas.DragSource="Button.Content" />
<Button Content="Drop here" Canvas.Top="300" Canvas.Left="300"
local:DragAndDropCanvas.DropDataFormat="{x:Static DataFormats.Text}"
local:DragAndDropCanvas.DropTarget="Button.Content"/>
</local:DragAndDropCanvas>
</Window>
這樣就完成一個簡單的可拖曳 Canvas,完整範例在此。