使用自定义控件拖放到网格上

时间:2014-05-08 00:45:46

标签: c# wpf wpf-controls

我希望允许用户顺利地在Grid控件周围拖动自定义控件,而且我不确定我到底错过了什么。所有控件都将AllowDrop设置为true。

  1. MouseMove上,我在正在拖动的控件中执行以下操作:

    DataObject dataObj = new DataObject("PersistentObject",this);
    DragDrop.DoDragDrop(this, dataObj, DragDropEffects.Move);
    
  2. 在网格的DragEnterDragOverDragDrop事件中,我将DragEventArg的效果设置为全部。
  3. 反馈显示网格上的新位置是一个有效的放置目标,但似乎永远不会移动。

    有没有办法在网格上执行此操作,或者我尝试在错误的控件上执行此操作(我使用的是网格,因为设计人员开始使用网格)?

    是否有其他事件需要修复,和/或我现有的事件是否已损坏?

    编辑:如果它显示正在发生的控件被拖动,也会受到赞赏。我不确定我的当前方法是否应该发生这种情况,但这就是目标。

4 个答案:

答案 0 :(得分:2)

使用内置的拖放功能,您可以轻松地执行

Point mouseOffset = new Point();

userControl.PreviewMouseLeftButtonDown += (sender, e) =>
{
    mouseOffset = Mouse.GetPosition(userControl);
    userControl.CaptureMouse();
};

userControl.PreviewMouseMove += (sender, e) =>
{
    if (userControl.IsMouseCaptured)
    {
        Point mouseDelta = Mouse.GetPosition(userControl);
        mouseDelta.Offset(-mouseOffset.X, -mouseOffset.Y);

        userControl.Margin = new Thickness(
            userControl.Margin.Left + mouseDelta.X,
            userControl.Margin.Top + mouseDelta.Y,
            userControl.Margin.Right - mouseDelta.X,
            userControl.Margin.Bottom - mouseDelta.Y);
    }
};

userControl.PreviewMouseLeftButtonUp += (sender, e) =>
{
    userControl.ReleaseMouseCapture();
};

所以我为此编写的演示代码如下所示:

<Window x:Class="StackOverflowScratchpad.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="350"
    Width="525">
    <Grid>
        <!-- I am using a Button here just for simplicity. The code
             will function the same no matter what control is used. -->
        <Button x:Name="userControl" Width="75" Height="23" />
    </Grid>
</Window>

public MainWindow()
{
    InitializeComponent();

    Loaded += MainWindow_Loaded;
}

private void MainWindow_Loaded(object obj, RoutedEventArgs args)
{
    // Add the above C# here.
}

答案 1 :(得分:1)

我建议使用Canvas控件。下面是一个示例Canvas控件,您放入其中的任何内容都将允许用户拖动以及调整大小:

不是原始代码不是我的,虽然我对它做了一些修改,这是我在网上找到的东西。 (我很乐意在这里引用消息来源,但不幸的是,我很久以前就被遗忘了。)

希望这会有所帮助,或者至少指出你正确的方向指向你。

<强> SuperCanvas:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using Meld.Helpers;

namespace Meld.Controls
{
    public class SuperCanvas : Canvas
    {
        private AdornerLayer aLayer;

        private bool isDown;
        private bool isDragging;
        private double originalLeft;
        private double originalTop;
        private bool selected;
        private UIElement selectedElement;

        private Point startPoint;

        private bool locked;

        public SuperCanvas()
        {
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            MouseLeftButtonDown += WorkspaceWindow_MouseLeftButtonDown;
            MouseLeftButtonUp += DragFinishedMouseHandler;
            MouseMove += WorkspaceWindow_MouseMove;
            MouseLeave += Window1_MouseLeave;

            PreviewMouseLeftButtonDown += myCanvas_PreviewMouseLeftButtonDown;
            PreviewMouseLeftButtonUp += DragFinishedMouseHandler;
        }

        // Handler for drag stopping on leaving the window
        private void Window1_MouseLeave(object sender, MouseEventArgs e)
        {
            StopDragging();
            e.Handled = true;
        }

        // Handler for drag stopping on user choice
        private void DragFinishedMouseHandler(object sender, MouseButtonEventArgs e)
        {
            StopDragging();
            e.Handled = true;
        }

        // Method for stopping dragging
        private void StopDragging()
        {
            if (isDown)
            {
                isDown = false;
                isDragging = false;
            }
        }

        // Hanler for providing drag operation with selected element
        private void WorkspaceWindow_MouseMove(object sender, MouseEventArgs e)
        {
            if (locked) return;

            if (isDown)
            {
                if ((isDragging == false) &&
                    ((Math.Abs(e.GetPosition(this).X - startPoint.X) >
                      SystemParameters.MinimumHorizontalDragDistance) ||
                     (Math.Abs(e.GetPosition(this).Y - startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)))
                    isDragging = true;

                if (isDragging)
                {
                    Point position = Mouse.GetPosition(this);
                    SetTop(selectedElement, position.Y - (startPoint.Y - originalTop));
                    SetLeft(selectedElement, position.X - (startPoint.X - originalLeft));
                }
            }
        }

        // Handler for clearing element selection, adorner removal
        private void WorkspaceWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (locked) return;

            if (selected)
            {
                selected = false;
                if (selectedElement != null)
                {
                    aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
                    selectedElement = null;
                }
            }
        }

        // Handler for element selection on the canvas providing resizing adorner
        private void myCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //add code to lock dragging and dropping things.
            if (locked)
            {
                e.Handled = true;
                return;
            }

            // Remove selection on clicking anywhere the window
            if (selected)
            {
                selected = false;
                if (selectedElement != null)
                {
                    // Remove the adorner from the selected element
                    aLayer.Remove(aLayer.GetAdorners(selectedElement)[0]);
                    selectedElement = null;
                }
            }

            // If any element except canvas is clicked, 
            // assign the selected element and add the adorner
            if (e.Source != this)
            {
                isDown = true;
                startPoint = e.GetPosition(this);

                selectedElement = e.Source as UIElement;

                originalLeft = GetLeft(selectedElement);
                originalTop = GetTop(selectedElement);

                aLayer = AdornerLayer.GetAdornerLayer(selectedElement);
                aLayer.Add(new ResizingAdorner(selectedElement));
                selected = true;
                e.Handled = true;
            }
        }
    }
}

ResizingAdorner:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace Meld.Helpers
{
    internal class ResizingAdorner : Adorner
    {
        // Resizing adorner uses Thumbs for visual elements.  
        // The Thumbs have built-in mouse input handling.
        Thumb topLeft, topRight, bottomLeft, bottomRight;

        // To store and manage the adorner's visual children.
        VisualCollection visualChildren;

        // Initialize the ResizingAdorner.
        public ResizingAdorner(UIElement adornedElement) : base(adornedElement)
        {                
            visualChildren = new VisualCollection(this);

            // Call a helper method to initialize the Thumbs
            // with a customized cursors.
            BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE);
            BuildAdornerCorner(ref topRight, Cursors.SizeNESW);
            BuildAdornerCorner(ref bottomLeft, Cursors.SizeNESW);
            BuildAdornerCorner(ref bottomRight, Cursors.SizeNWSE);

            // Add handlers for resizing.
            bottomLeft.DragDelta += new DragDeltaEventHandler(HandleBottomLeft);
            bottomRight.DragDelta += new DragDeltaEventHandler(HandleBottomRight);
            topLeft.DragDelta += new DragDeltaEventHandler(HandleTopLeft);
            topRight.DragDelta += new DragDeltaEventHandler(HandleTopRight);
        }

        // Handler for resizing from the bottom-right.
        void HandleBottomRight(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;
            FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
            adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);
        }

        // Handler for resizing from the top-right.
        void HandleTopRight(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = this.AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;
            FrameworkElement parentElement = adornedElement.Parent as FrameworkElement;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            adornedElement.Width = Math.Max(adornedElement.Width + args.HorizontalChange, hitThumb.DesiredSize.Width);
            //adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);

            double height_old = adornedElement.Height;
            double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
            double top_old = Canvas.GetTop(adornedElement);
            adornedElement.Height = height_new;
            Canvas.SetTop(adornedElement, top_old - (height_new - height_old));
        }

        // Handler for resizing from the top-left.
        void HandleTopLeft(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            //adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            //adornedElement.Height = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);

            double width_old = adornedElement.Width;
            double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            double left_old = Canvas.GetLeft(adornedElement);
            adornedElement.Width = width_new;
            Canvas.SetLeft(adornedElement, left_old - (width_new - width_old));

            double height_old = adornedElement.Height;
            double height_new = Math.Max(adornedElement.Height - args.VerticalChange, hitThumb.DesiredSize.Height);
            double top_old = Canvas.GetTop(adornedElement);
            adornedElement.Height = height_new;
            Canvas.SetTop(adornedElement, top_old - (height_new - height_old));
        }

        // Handler for resizing from the bottom-left.
        void HandleBottomLeft(object sender, DragDeltaEventArgs args)
        {
            FrameworkElement adornedElement = AdornedElement as FrameworkElement;
            Thumb hitThumb = sender as Thumb;

            if (adornedElement == null || hitThumb == null) return;

            // Ensure that the Width and Height are properly initialized after the resize.
            EnforceSize(adornedElement);

            // Change the size by the amount the user drags the mouse, as long as it's larger 
            // than the width or height of an adorner, respectively.
            //adornedElement.Width = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            adornedElement.Height = Math.Max(args.VerticalChange + adornedElement.Height, hitThumb.DesiredSize.Height);

            double width_old = adornedElement.Width;
            double width_new = Math.Max(adornedElement.Width - args.HorizontalChange, hitThumb.DesiredSize.Width);
            double left_old = Canvas.GetLeft(adornedElement);
            adornedElement.Width = width_new;            
            Canvas.SetLeft(adornedElement, left_old - (width_new - width_old));
        }

        // Arrange the Adorners.
        protected override Size ArrangeOverride(Size finalSize)
        {
            // desiredWidth and desiredHeight are the width and height of the element that's being adorned.  
            // These will be used to place the ResizingAdorner at the corners of the adorned element.  
            double desiredWidth = AdornedElement.DesiredSize.Width;
            double desiredHeight = AdornedElement.DesiredSize.Height;
            // adornerWidth & adornerHeight are used for placement as well.
            double adornerWidth = this.DesiredSize.Width;
            double adornerHeight = this.DesiredSize.Height;

            topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            topRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
            bottomLeft.Arrange(new Rect(-adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));
            bottomRight.Arrange(new Rect(desiredWidth - adornerWidth / 2, desiredHeight - adornerHeight / 2, adornerWidth, adornerHeight));

            // Return the final size.
            return finalSize;
        }

        // Helper method to instantiate the corner Thumbs, set the Cursor property, 
        // set some appearance properties, and add the elements to the visual tree.
        void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
        {
            if (cornerThumb != null) return;

            cornerThumb = new Thumb();

            // Set some arbitrary visual characteristics.
            cornerThumb.Cursor = customizedCursor;
            cornerThumb.Height = cornerThumb.Width = 5;
            cornerThumb.Opacity = 0.40;
            cornerThumb.Background = new SolidColorBrush(Colors.White);

            visualChildren.Add(cornerThumb);
        }

        // This method ensures that the Widths and Heights are initialized.  Sizing to content produces
        // Width and Height values of Double.NaN.  Because this Adorner explicitly resizes, the Width and Height
        // need to be set first.  It also sets the maximum size of the adorned element.
        void EnforceSize(FrameworkElement adornedElement)
        {
            if (adornedElement.Width.Equals(Double.NaN))
                adornedElement.Width = adornedElement.DesiredSize.Width;
            if (adornedElement.Height.Equals(Double.NaN))
                adornedElement.Height = adornedElement.DesiredSize.Height;

            FrameworkElement parent = adornedElement.Parent as FrameworkElement;
            if (parent != null)
            {
                adornedElement.MaxHeight = parent.ActualHeight;
                adornedElement.MaxWidth = parent.ActualWidth;
            }
        }
        // Override the VisualChildrenCount and GetVisualChild properties to interface with 
        // the adorner's visual collection.
        protected override int VisualChildrenCount { get { return visualChildren.Count; } }
        protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
    }
}

答案 2 :(得分:0)

你必须处理代码自己的Drop事件

Drop事件在对象作为放置目标的元素的边界内被删除时发生。

private void grid_Drop(object sender, DragEventArgs eventarg)
 {
     // check data is present returns a bool
 if (eventarg.Data.GetDataPresent("PersistentObject"))
            {
                var yourdata=eventarg.Data.GetData("PersistentObject")as Model(T) ;
                if (yourdata!= null)
                {
                  // add to gird or whatever.  
                     // place the object as you desire
                }

 }   

一些拖放Reference

答案 3 :(得分:0)

几年前我创建了一个DragDropBehaviour类。您应该能够复制粘贴它,只需更改OnDrop功能即可。

public static class DragDropBehaviour
{
    #region CanDrag attached dependancy property
    /// <summary>
    /// The CanDrag attached property'str name.
    /// </summary>
    public const string CanDragPropertyName = "CanDrag";

    /// <summary>
    /// Gets the value of the CanDrag attached property 
    /// for a given dependency object.
    /// </summary>
    /// <param name="obj">The object for which the property value
    /// is read.</param>
    /// <returns>The value of the CanDrag property of the specified object.</returns>
    public static bool GetCanDrag(DependencyObject obj)
    {
        return (bool)obj.GetValue(CanDragProperty);
    }

    /// <summary>
    /// Sets the value of the CanDrag attached property
    /// for a given dependency object. 
    /// </summary>
    /// <param name="obj">The object to which the property value
    /// is written.</param>
    /// <param name="value">Sets the CanDrag value of the specified object.</param>
    public static void SetCanDrag(DependencyObject obj, bool value)
    {
        obj.SetValue(CanDragProperty, value);
    }

    /// <summary>
    /// Identifies the CanDrag attached property.
    /// </summary>
    public static readonly DependencyProperty CanDragProperty = DependencyProperty.RegisterAttached(
        CanDragPropertyName,
        typeof(bool),
        typeof(DragDropBehaviour),
        new UIPropertyMetadata(false, OnCanDragChanged));
    #endregion

    #region CanDrop attached dependancy property
    /// <summary>
    /// The CanDrop attached property'str name.
    /// </summary>
    public const string CanDropPropertyName = "CanDrop";

    /// <summary>
    /// Gets the value of the CanDrop attached property 
    /// for a given dependency object.
    /// </summary>
    /// <param name="obj">The object for which the property value
    /// is read.</param>
    /// <returns>The value of the CanDrop property of the specified object.</returns>
    public static bool GetCanDrop(DependencyObject obj)
    {
        return (bool)obj.GetValue(CanDropProperty);
    }

    /// <summary>
    /// Sets the value of the CanDrop attached property
    /// for a given dependency object. 
    /// </summary>
    /// <param name="obj">The object to which the property value
    /// is written.</param>
    /// <param name="value">Sets the CanDrop value of the specified object.</param>
    public static void SetCanDrop(DependencyObject obj, bool value)
    {
        obj.SetValue(CanDropProperty, value);
    }

    /// <summary>
    /// Identifies the CanDrop attached property.
    /// </summary>
    public static readonly DependencyProperty CanDropProperty = DependencyProperty.RegisterAttached(
        CanDropPropertyName,
        typeof(bool),
        typeof(DragDropBehaviour),
        new UIPropertyMetadata(false, OnCanDropChanged));
    #endregion

    #region DragDropAction attached dependancy property
    /// <summary>
    /// The DragDropAction attached property'str name.
    /// </summary>
    public const string DragDropActionPropertyName = "DragDropAction";

    /// <summary>
    /// Gets the value of the DragDropAction attached property 
    /// for a given dependency object.
    /// </summary>
    /// <param name="obj">The object for which the property value
    /// is read.</param>
    /// <returns>The value of the DragDropAction property of the specified object.</returns>
    public static DragDropAction GetDragDropAction(DependencyObject obj)
    {
        return (DragDropAction)obj.GetValue(DragDropActionProperty);
    }

    /// <summary>
    /// Sets the value of the DragDropAction attached property
    /// for a given dependency object. 
    /// </summary>
    /// <param name="obj">The object to which the property value
    /// is written.</param>
    /// <param name="value">Sets the DragDropAction value of the specified object.</param>
    public static void SetDragDropAction(DependencyObject obj, DragDropAction value)
    {
        obj.SetValue(DragDropActionProperty, value);
    }

    /// <summary>
    /// Identifies the DragDropAction attached property.
    /// </summary>
    public static readonly DependencyProperty DragDropActionProperty = DependencyProperty.RegisterAttached(
        DragDropActionPropertyName, 
        typeof(DragDropAction),
        typeof(DragDropBehaviour), 
        new UIPropertyMetadata(null));
    #endregion

    static void OnCanDragChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement element = depObj as FrameworkElement;

        if (element != null)
        {
            if ((bool)e.NewValue)
            {
                GetOrCreateAction(depObj).DragBehaviour(element, true);
            }
            else
            {
                GetOrCreateAction(depObj).DragBehaviour(element, false);                    
            }
        }
    }

    static void OnCanDropChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement element = depObj as FrameworkElement;

        if (element != null)
        {
            if ((bool)e.NewValue)
            {
                GetOrCreateAction(depObj).DropBehaviour(element, true);
            }
            else
            {
                GetOrCreateAction(depObj).DropBehaviour(element, false);
            }
        }
    }

    static DragDropAction GetOrCreateAction(DependencyObject depObj)
    {
        DragDropAction action = depObj.GetValue(DragDropActionProperty) as DragDropAction;
        if (action == null)
        {
            action = new DragDropAction();
            depObj.SetValue(DragDropActionProperty, action);
        }
        return action;
    }
}

public class DragDropAction
{
    Point _start;
    FrameworkElement _dragged;

    public void DragBehaviour(FrameworkElement element, bool enable)
    {
        if (enable)
        {
            element.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
            element.PreviewMouseMove += OnPreviewMouseMove;
        }
        else
        {
            element.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown;
            element.PreviewMouseMove -= OnPreviewMouseMove;
        }
    }

    public void DropBehaviour(FrameworkElement element, bool enable)
    {
        if (enable)
        {
            element.Drop += OnDrop;
            element.AllowDrop = true;
        }
        else
        {
            element.Drop -= OnDrop;
            element.AllowDrop = false;
        }
    }

    void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        FrameworkElement element = sender as FrameworkElement;
        if (element != null)
        {
            int[] position = Win32Mouse.GetMousePosition();
            _start = new Point(position[0], position[1]);
            _dragged = element;
        }
    }

    void OnPreviewMouseMove(object sender, MouseEventArgs e)
    {
        FrameworkElement element = sender as FrameworkElement;
        if (element != null && _dragged != null)
        {
            int[] position = Win32Mouse.GetMousePosition();
            Point currentPosition = new Point(position[0], position[1]);
            Vector diff = _start - currentPosition;

            if (e.LeftButton == MouseButtonState.Pressed &&
                Math.Abs(diff.X) > (SystemParameters.MinimumHorizontalDragDistance) &&
                Math.Abs(diff.Y) > (SystemParameters.MinimumVerticalDragDistance))
            {
                DragDropEffects effects = DragDrop.DoDragDrop(element, _dragged.DataContext, DragDropEffects.Move);
                _dragged = null;
                e.Handled = true;
            }
        }
    }

    void OnDrop(object sender, DragEventArgs e)
    {
        FrameworkElement element = sender as FrameworkElement;
        if (element != null)
        {
            TabViewModel tab = element.DataContext as TabViewModel;
            if (tab == null)
            {
                // TabViewModel not found it element, it's possible that the drop was done on the lastOpened 'Canvas' element.
                var tabControls = element.FindVisualChildren<TabControl>();
                var menus = element.FindVisualChildren<Menu>();
                var itemControls = element.FindVisualChildren<ItemsControl>();

                if (tabControls.Count > 0 && tabControls[0].Visibility == Visibility.Visible)
                {
                    // If currently in 'horizontal mode' add to the active tab. If there is no active tab
                    // just add to the bottom tab.
                    tab = tabControls[0].SelectedItem as TabViewModel;
                    if (tab == null)                        
                        tab = tabControls[0].Items.GetItemAt(tabControls[0].Items.Count - 1) as TabViewModel;                        
                }
                else if (menus.Count > 0 && menus[0].Visibility == Visibility.Visible)
                {
                    // If currently in 'vertical mode' add to the default tab, there is no 'active' menu item after all.
                    var tabs = menus[0].Items.SourceCollection as ObservableCollection<TabViewModel>;
                    tab = tabs.SingleOrDefault(obj => obj.Title == Configuration.DefaultTab) ?? tabs.LastOrDefault();
                }
                else if (itemControls.Count > 0 && itemControls[0].Visibility == Visibility.Visible)
                {
                    var window = element.FindVisualParent<Window>();
                    if (window != null && window.DataContext is MainViewModel)
                    {
                        // Add the currently expanded tab.
                        MainViewModel mainViewModel = (MainViewModel)window.DataContext;
                        tab = mainViewModel.ExpandedTab;

                        // If no tab is expanded, add to the default tab or the bottom tab.
                        if (tab == null)
                        {
                            tab = mainViewModel.Tabs.SingleOrDefault(obj => obj.Title == Configuration.DefaultTab)
                                    ?? mainViewModel.Tabs.LastOrDefault();
                        }
                    }
                }
            }

            if (tab != null)
            {
                if (e.Data.GetDataPresent(DataFormats.FileDrop))
                {                        
                    DispatcherHelper.UIDispatcher.BeginInvoke(new Action(() =>
                        {
                            string[] droppedFilePaths = e.Data.GetData(DataFormats.FileDrop, true) as string[];
                            foreach (string fileName in droppedFilePaths)
                            {
                                try
                                {

                                    ApplicationModel model = ApplicationModel.FromFile(fileName, tab.Title);
                                    ApplicationViewModel application = new ApplicationViewModel(model);
                                    Messenger.Default.Send<ApplicationMessage>(ApplicationMessage.Add(application, tab));
                                }
                                catch (FileNotFoundException ex)
                                {
                                    ServiceManager.GetService<IMessageBoxService>().Show(
                                        "Could not add application - " + ex.Message, "Astounding Dock", MessageIcon.Error);
                                }
                            }
                        }));
                    e.Handled = true;
                }
                else if (e.Data.GetDataPresent<ApplicationViewModel>())
                {
                    DispatcherHelper.UIDispatcher.BeginInvoke(new Action(() =>
                        {
                            ApplicationViewModel application = e.Data.GetData<ApplicationViewModel>();
                            Messenger.Default.Send<ApplicationMessage>(ApplicationMessage.Move(application, tab));
                        }));
                    e.Handled = true;
                }
                else
                {
                    Debug.WriteLine("DragDropBehaviour: Unknown data droppped - " + String.Join(",", e.Data.GetFormats()));
                }
            }
        }
    }
}

https://github.com/notsonormal/AstoundingDock/blob/master/src/AstoundingDock/Ui/DragDropBehaviour.cs

您可以启用像

这样的控件上的删除功能
<Canvas ui:DragDropBehaviour.CanDrop="True">
</Canvas>

并启用这样的拖放

<Button ui:DragDropBehaviour.CanDrag="True" ui:DragDropBehaviour.CanDrop="True">
</Button>

https://github.com/notsonormal/AstoundingDock/blob/master/src/AstoundingDock/Views/MainWindow.xaml

看起来非常通用,但我只是将它用于一个特定情况,所以......