将自定义控件拖放到网格上

本文关键字:网格 拖放 自定义控件 | 更新日期: 2023-09-27 18:02:29

我希望允许用户在Grid控件周围平稳地拖动自定义控件,我不确定我究竟缺少什么。所有控件的AllowDrop设置为true。

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

    DataObject dataObj = new DataObject("PersistentObject",this);
    DragDrop.DoDragDrop(this, dataObj, DragDropEffects.Move);
    
  2. 在Grid的DragEnter, DragOverDragDrop事件中,我将DragEventArg的效果设置为all。

反馈显示,如果网格上的新位置是一个有效的掉落目标,但它似乎永远不会移动。

是否有一种方法可以在网格上做到这一点,或者我试图在错误的控件上做到这一点(我正在使用网格,因为设计师开始使用一个)?

是否有我必须修复的其他事件,和/或我当前的事件坏了?

Edit:如果显示控件在发生时被拖动,那也将是值得赞赏的。我不确定这是否应该发生在我目前的方法,但这是目标。

将自定义控件拖放到网格上

您可以轻松地完成,而不需要使用内置的拖放工具

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.
}

我建议使用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]; }
    }
}

你必须处理代码自己的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
                }
 }   

拖放参考

几年前我创建了一个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

它看起来很通用,但我只在一个特殊情况下使用过它,所以…