[WPF] Darg and Drop (2)

  • 49
  • 0

上一篇介紹了使用事件為主的 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,完整範例在此