在WPF DataGrid上拖动Drop Row行为

时间:2012-05-08 06:11:02

标签: c# wpf drag-and-drop wpfdatagrid attachedbehaviors

我正在尝试通过执行Drag & Drop

来制作附加行为以重新排序行

我找到了一些解决方案(在Stackoverflow上并通过谷歌搜索)并使用它们我试图做出这个行为......我从Hordcodenet网站上拿了例子(我现在没有链接)

代码

public static class DragDropRowBehavior
{
    private static DataGrid dataGrid;

    private static Popup popup;

    private static bool enable;

    private static object draggedItem;

    public static object DraggedItem
    {
        get { return DragDropRowBehavior.draggedItem; }
        set { DragDropRowBehavior.draggedItem = value; }
    }

    public static Popup GetPopupControl(DependencyObject obj)
    {
        return (Popup)obj.GetValue(PopupControlProperty);
    }

    public static void SetPopupControl(DependencyObject obj, Popup value)
    {
        obj.SetValue(PopupControlProperty, value);
    }

    // Using a DependencyProperty as the backing store for PopupControl.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PopupControlProperty =
        DependencyProperty.RegisterAttached("PopupControl", typeof(Popup), typeof(DragDropRowBehavior), new UIPropertyMetadata(null, OnPopupControlChanged));

    private static void OnPopupControlChanged(DependencyObject depObject, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue == null || !(e.NewValue is Popup))
        {
            throw new ArgumentException("Popup Control should be set", "PopupControl");
        }
        popup = e.NewValue as Popup;

        dataGrid = depObject as DataGrid;
        // Check if DataGrid
        if (dataGrid == null)
            return;


        if (enable && popup != null)
        {
            dataGrid.BeginningEdit += new EventHandler<DataGridBeginningEditEventArgs>(OnBeginEdit);
            dataGrid.CellEditEnding += new EventHandler<DataGridCellEditEndingEventArgs>(OnEndEdit);
            dataGrid.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp);
            dataGrid.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
            dataGrid.MouseMove += new MouseEventHandler(OnMouseMove);
        }
        else
        {
            dataGrid.BeginningEdit -= new EventHandler<DataGridBeginningEditEventArgs>(OnBeginEdit);
            dataGrid.CellEditEnding -= new EventHandler<DataGridCellEditEndingEventArgs>(OnEndEdit);
            dataGrid.MouseLeftButtonUp -= new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp);
            dataGrid.MouseLeftButtonDown -= new MouseButtonEventHandler(OnMouseLeftButtonDown);
            dataGrid.MouseMove -= new MouseEventHandler(OnMouseMove);

            dataGrid = null;
            popup = null;
            draggedItem = null;
            IsEditing = false;
            IsDragging = false;
        }
    }

    public static bool GetEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(EnabledProperty);
    }

    public static void SetEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(EnabledProperty, value);
    }

    // Using a DependencyProperty as the backing store for Enabled.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty EnabledProperty =
        DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(DragDropRowBehavior), new UIPropertyMetadata(false,OnEnabledChanged));

    private static void OnEnabledChanged(DependencyObject depObject,DependencyPropertyChangedEventArgs e)
    {
        //Check if value is a Boolean Type
        if (e.NewValue is bool == false)
            throw new ArgumentException("Value should be of bool type", "Enabled");

        enable = (bool)e.NewValue;

    }

    public static bool IsEditing { get; set; }

    public static bool IsDragging { get; set; }

    private static void OnBeginEdit(object sender, DataGridBeginningEditEventArgs e)
    {
        IsEditing = true;
        //in case we are in the middle of a drag/drop operation, cancel it...
        if (IsDragging) ResetDragDrop();
    }

    private static void OnEndEdit(object sender, DataGridCellEditEndingEventArgs e)
    {
        IsEditing = false;
    }


    /// <summary>
    /// Initiates a drag action if the grid is not in edit mode.
    /// </summary>
    private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (IsEditing) return;

        var row = UIHelpers.TryFindFromPoint<DataGridRow>((UIElement)sender, e.GetPosition(dataGrid));
        if (row == null || row.IsEditing) return;

        //set flag that indicates we're capturing mouse movements
        IsDragging = true;
        DraggedItem = row.Item;
    }

    /// <summary>
    /// Completes a drag/drop operation.
    /// </summary>
    private static void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (!IsDragging || IsEditing)
        {
            return;
        }

        //get the target item
        var targetItem = dataGrid.SelectedItem;

        if (targetItem == null || !ReferenceEquals(DraggedItem, targetItem))
        {
            //remove the source from the list
            ((dataGrid).ItemsSource as IList).Remove(DraggedItem);

            //get target index
            var targetIndex = ((dataGrid).ItemsSource as IList).IndexOf(targetItem);

            //move source at the target's location
            ((dataGrid).ItemsSource as IList).Insert(targetIndex, DraggedItem);

            //select the dropped item
            dataGrid.SelectedItem = DraggedItem;
        }

        //reset
        ResetDragDrop();
    }

    /// <summary>
    /// Closes the popup and resets the
    /// grid to read-enabled mode.
    /// </summary>
    private static void ResetDragDrop()
    {
        IsDragging = false;
        popup.IsOpen = false;
        dataGrid.IsReadOnly = false;
    }

    /// <summary>
    /// Updates the popup's position in case of a drag/drop operation.
    /// </summary>
    private static void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (!IsDragging || e.LeftButton != MouseButtonState.Pressed) return;

        //display the popup if it hasn't been opened yet
        if (!popup.IsOpen)
        {
            //switch to read-only mode
            dataGrid.IsReadOnly = true;

            //make sure the popup is visible
            popup.IsOpen = true;
        }


        Size popupSize = new Size(popup.ActualWidth, popup.ActualHeight);
        popup.PlacementRectangle = new Rect(e.GetPosition(dataGrid), popupSize);

        //make sure the row under the grid is being selected
        Point position = e.GetPosition(dataGrid);
        var row = UIHelpers.TryFindFromPoint<DataGridRow>(dataGrid, position);
        if (row != null) dataGrid.SelectedItem = row.Item;
    }

}

助手类

///   ///与UI相关的辅助方法。   ///   public static class UIHelpers   {

#region find parent

/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(DependencyObject child)
  where T : DependencyObject
{
  //get parent item
  DependencyObject parentObject = GetParentObject(child);

  //we've reached the end of the tree
  if (parentObject == null) return null;

  //check if the parent matches the type we're looking for
  T parent = parentObject as T;
  if (parent != null)
  {
    return parent;
  }
  else
  {
    //use recursion to proceed with next level
    return TryFindParent<T>(parentObject);
  }
}


/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
/// supports content elements. Do note, that for content element,
/// this method falls back to the logical tree of the element.
/// </summary>
/// <param name="child">The item to be processed.</param>
/// <returns>The submitted item's parent, if available. Otherwise
/// null.</returns>
public static DependencyObject GetParentObject(DependencyObject child)
{
  if (child == null) return null;
  ContentElement contentElement = child as ContentElement;

  if (contentElement != null)
  {
    DependencyObject parent = ContentOperations.GetParent(contentElement);
    if (parent != null) return parent;

    FrameworkContentElement fce = contentElement as FrameworkContentElement;
    return fce != null ? fce.Parent : null;
  }

  //if it's not a ContentElement, rely on VisualTreeHelper
  return VisualTreeHelper.GetParent(child);
}

#endregion


#region update binding sources

/// <summary>
/// Recursively processes a given dependency object and all its
/// children, and updates sources of all objects that use a
/// binding expression on a given property.
/// </summary>
/// <param name="obj">The dependency object that marks a starting
/// point. This could be a dialog window or a panel control that
/// hosts bound controls.</param>
/// <param name="properties">The properties to be updated if
/// <paramref name="obj"/> or one of its childs provide it along
/// with a binding expression.</param>
public static void UpdateBindingSources(DependencyObject obj,
                          params DependencyProperty[] properties)
{
  foreach (DependencyProperty depProperty in properties)
  {
    //check whether the submitted object provides a bound property
    //that matches the property parameters
    BindingExpression be = BindingOperations.GetBindingExpression(obj, depProperty);
    if (be != null) be.UpdateSource();
  }

  int count = VisualTreeHelper.GetChildrenCount(obj);
  for (int i = 0; i < count; i++)
  {
    //process child items recursively
    DependencyObject childObject = VisualTreeHelper.GetChild(obj, i);
    UpdateBindingSources(childObject, properties);
  }
}

#endregion


/// <summary>
/// Tries to locate a given item within the visual tree,
/// starting with the dependency object at a given position. 
/// </summary>
/// <typeparam name="T">The type of the element to be found
/// on the visual tree of the element at the given location.</typeparam>
/// <param name="reference">The main element which is used to perform
/// hit testing.</param>
/// <param name="point">The position to be evaluated on the origin.</param>
public static T TryFindFromPoint<T>(UIElement reference, Point point)
  where T : DependencyObject
{
  DependencyObject element = reference.InputHitTest(point)
                               as DependencyObject;
  if (element == null) return null;
  else if (element is T) return (T)element;
  else return TryFindParent<T>(element);
}

}

问题是当我按下一行来拖动它时,不会调用事件OnMouseLeftButtonDown ...但在此之后调用OnMouseLeftButtonUp ....

有没有办法做到这一点......

我似乎找不到方法

3 个答案:

答案 0 :(得分:8)

最后我得到了问题,并为此做了一些改变才能正常工作

我使用this示例让Drag Drop Logic amd使得这种行为可能对其他人有用......请提出改进​​建议我很乐意改变......

行为

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using Microsoft.Windows.Controls;
using System.Windows.Input;
using System.Collections;

namespace DataGridDragAndDrop
{
public static class DragDropRowBehavior
{
    private static DataGrid dataGrid;

    private static Popup popup;

    private static bool enable;

    private static object draggedItem;

    public static object DraggedItem
    {
        get { return DragDropRowBehavior.draggedItem; }
        set { DragDropRowBehavior.draggedItem = value; }
    }

    public static Popup GetPopupControl(DependencyObject obj)
    {
        return (Popup)obj.GetValue(PopupControlProperty);
    }

    public static void SetPopupControl(DependencyObject obj, Popup value)
    {
        obj.SetValue(PopupControlProperty, value);
    }

    // Using a DependencyProperty as the backing store for PopupControl.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PopupControlProperty =
        DependencyProperty.RegisterAttached("PopupControl", typeof(Popup), typeof(DragDropRowBehavior), new UIPropertyMetadata(null, OnPopupControlChanged));

    private static void OnPopupControlChanged(DependencyObject depObject, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue == null || !(e.NewValue is Popup))
        {
            throw new ArgumentException("Popup Control should be set", "PopupControl");
        }
        popup = e.NewValue as Popup;

        dataGrid = depObject as DataGrid;
        // Check if DataGrid
        if (dataGrid == null)
            return;


        if (enable && popup != null)
        {
            dataGrid.BeginningEdit += new EventHandler<DataGridBeginningEditEventArgs>(OnBeginEdit);
            dataGrid.CellEditEnding += new EventHandler<DataGridCellEditEndingEventArgs>(OnEndEdit);
            dataGrid.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp);
            dataGrid.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
            dataGrid.MouseMove += new MouseEventHandler(OnMouseMove);
        }
        else
        {
            dataGrid.BeginningEdit -= new EventHandler<DataGridBeginningEditEventArgs>(OnBeginEdit);
            dataGrid.CellEditEnding -= new EventHandler<DataGridCellEditEndingEventArgs>(OnEndEdit);
            dataGrid.MouseLeftButtonUp -= new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp);
            dataGrid.MouseLeftButtonDown -= new MouseButtonEventHandler(OnMouseLeftButtonDown);
            dataGrid.MouseMove -= new MouseEventHandler(OnMouseMove);

            dataGrid = null;
            popup = null;
            draggedItem = null;
            IsEditing = false;
            IsDragging = false;
        }
    }

    public static bool GetEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(EnabledProperty);
    }

    public static void SetEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(EnabledProperty, value);
    }

    // Using a DependencyProperty as the backing store for Enabled.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty EnabledProperty =
        DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(DragDropRowBehavior), new UIPropertyMetadata(false,OnEnabledChanged));

    private static void OnEnabledChanged(DependencyObject depObject,DependencyPropertyChangedEventArgs e)
    {
        //Check if value is a Boolean Type
        if (e.NewValue is bool == false)
            throw new ArgumentException("Value should be of bool type", "Enabled");

        enable = (bool)e.NewValue;

    }

    public static bool IsEditing { get; set; }

    public static bool IsDragging { get; set; }

    private static void OnBeginEdit(object sender, DataGridBeginningEditEventArgs e)
    {
        IsEditing = true;
        //in case we are in the middle of a drag/drop operation, cancel it...
        if (IsDragging) ResetDragDrop();
    }

    private static void OnEndEdit(object sender, DataGridCellEditEndingEventArgs e)
    {
        IsEditing = false;
    }


    /// <summary>
    /// Initiates a drag action if the grid is not in edit mode.
    /// </summary>
    private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (IsEditing) return;

        var row = UIHelpers.TryFindFromPoint<DataGridRow>((UIElement)sender, e.GetPosition(dataGrid));
        if (row == null || row.IsEditing) return;

        //set flag that indicates we're capturing mouse movements
        IsDragging = true;
        DraggedItem = row.Item;
    }

    /// <summary>
    /// Completes a drag/drop operation.
    /// </summary>
    private static void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (!IsDragging || IsEditing)
        {
            return;
        }

        //get the target item
        var targetItem = dataGrid.SelectedItem;         

        if (targetItem == null || !ReferenceEquals(DraggedItem, targetItem))
        {
            //get target index
            var targetIndex = ((dataGrid).ItemsSource as IList).IndexOf(targetItem);

            //remove the source from the list
            ((dataGrid).ItemsSource as IList).Remove(DraggedItem);              

            //move source at the target's location
            ((dataGrid).ItemsSource as IList).Insert(targetIndex, DraggedItem);

            //select the dropped item
            dataGrid.SelectedItem = DraggedItem;
        }

        //reset
        ResetDragDrop();
    }

    /// <summary>
    /// Closes the popup and resets the
    /// grid to read-enabled mode.
    /// </summary>
    private static void ResetDragDrop()
    {
        IsDragging = false;
        popup.IsOpen = false;
        dataGrid.IsReadOnly = false;
    }

    /// <summary>
    /// Updates the popup's position in case of a drag/drop operation.
    /// </summary>
    private static void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (!IsDragging || e.LeftButton != MouseButtonState.Pressed) return;

        popup.DataContext = DraggedItem;
        //display the popup if it hasn't been opened yet
        if (!popup.IsOpen)
        {
            //switch to read-only mode
            dataGrid.IsReadOnly = true;

            //make sure the popup is visible
            popup.IsOpen = true;
        }


        Size popupSize = new Size(popup.ActualWidth, popup.ActualHeight);
        popup.PlacementRectangle = new Rect(e.GetPosition(dataGrid), popupSize);

        //make sure the row under the grid is being selected
        Point position = e.GetPosition(dataGrid);
        var row = UIHelpers.TryFindFromPoint<DataGridRow>(dataGrid, position);
        if (row != null) dataGrid.SelectedItem = row.Item;
    }

}
}

UIHelper Class

public static class UIHelpers
{

    #region find parent

    /// <summary>
    /// Finds a parent of a given item on the visual tree.
    /// </summary>
    /// <typeparam name="T">The type of the queried item.</typeparam>
    /// <param name="child">A direct or indirect child of the
    /// queried item.</param>
    /// <returns>The first parent item that matches the submitted
    /// type parameter. If not matching item can be found, a null
    /// reference is being returned.</returns>
    public static T TryFindParent<T>(DependencyObject child)
      where T : DependencyObject
    {
        //get parent item
        DependencyObject parentObject = GetParentObject(child);

        //we've reached the end of the tree
        if (parentObject == null) return null;

        //check if the parent matches the type we're looking for
        T parent = parentObject as T;
        if (parent != null)
        {
            return parent;
        }
        else
        {
            //use recursion to proceed with next level
            return TryFindParent<T>(parentObject);
        }
    }


    /// <summary>
    /// This method is an alternative to WPF's
    /// <see cref="VisualTreeHelper.GetParent"/> method, which also
    /// supports content elements. Do note, that for content element,
    /// this method falls back to the logical tree of the element.
    /// </summary>
    /// <param name="child">The item to be processed.</param>
    /// <returns>The submitted item's parent, if available. Otherwise
    /// null.</returns>
    public static DependencyObject GetParentObject(DependencyObject child)
    {
        if (child == null) return null;
        ContentElement contentElement = child as ContentElement;

        if (contentElement != null)
        {
            DependencyObject parent = ContentOperations.GetParent(contentElement);
            if (parent != null) return parent;

            FrameworkContentElement fce = contentElement as FrameworkContentElement;
            return fce != null ? fce.Parent : null;
        }

        //if it's not a ContentElement, rely on VisualTreeHelper
        return VisualTreeHelper.GetParent(child);
    }

    #endregion


    #region update binding sources

    /// <summary>
    /// Recursively processes a given dependency object and all its
    /// children, and updates sources of all objects that use a
    /// binding expression on a given property.
    /// </summary>
    /// <param name="obj">The dependency object that marks a starting
    /// point. This could be a dialog window or a panel control that
    /// hosts bound controls.</param>
    /// <param name="properties">The properties to be updated if
    /// <paramref name="obj"/> or one of its childs provide it along
    /// with a binding expression.</param>
    public static void UpdateBindingSources(DependencyObject obj,
                              params DependencyProperty[] properties)
    {
        foreach (DependencyProperty depProperty in properties)
        {
            //check whether the submitted object provides a bound property
            //that matches the property parameters
            BindingExpression be = BindingOperations.GetBindingExpression(obj, depProperty);
            if (be != null) be.UpdateSource();
        }

        int count = VisualTreeHelper.GetChildrenCount(obj);
        for (int i = 0; i < count; i++)
        {
            //process child items recursively
            DependencyObject childObject = VisualTreeHelper.GetChild(obj, i);
            UpdateBindingSources(childObject, properties);
        }
    }

    #endregion


    /// <summary>
    /// Tries to locate a given item within the visual tree,
    /// starting with the dependency object at a given position. 
    /// </summary>
    /// <typeparam name="T">The type of the element to be found
    /// on the visual tree of the element at the given location.</typeparam>
    /// <param name="reference">The main element which is used to perform
    /// hit testing.</param>
    /// <param name="point">The position to be evaluated on the origin.</param>
    public static T TryFindFromPoint<T>(UIElement reference, Point point)
      where T : DependencyObject
    {
        DependencyObject element = reference.InputHitTest(point)
                                     as DependencyObject;
        if (element == null) return null;
        else if (element is T) return (T)element;
        else return TryFindParent<T>(element);
    }
}

用法

    <!--  Drag and Drop Popup  -->
    <Popup x:Name="popup1"
           AllowsTransparency="True"
           IsHitTestVisible="False"
           Placement="RelativePoint"
           PlacementTarget="{Binding ElementName=shareGrid}">

    <!--  Your own Popup construction Use properties of DraggedObject inside for Binding  -->

                <TextBlock Margin="8,0,0,0"
                           VerticalAlignment="Center"
                           FontSize="14"
                           FontWeight="Bold"

    <!--  I used name property of in my Dragged row  -->

                           Text="{Binding Path=Name}" />
    </Popup>
    <DataGrid x:Name="myDataGrid"
                     AutoGenerateColumns="False"
                     CanUserAddRows="False"
                     CanUserDeleteRows="False"
                     CanUserReorderColumns="False"
                     CanUserSortColumns="False"
                     ItemsSource="{Binding}"
                     SelectionMode="Single"
                     this:DragDropRowBehavior.Enabled="True"
                     this:DragDropRowBehavior.PopupControl="{Binding ElementName=popup1}"></DataGrid >

答案 1 :(得分:1)

如果未在所需对象上触发事件,则是因为某些其他控件正在“吞咽”它。尝试使用OnPreviewMouseLeftButtonDown。如果预览没有显示事件,请尝试将该预览方法移动到父类。

答案 2 :(得分:0)

我用@Dave解决方案创建了一个行为:

 public class DragDropRowBehavior : Behavior<DataGrid>
{
    private object draggedItem;
    private bool isEditing;
    private bool isDragging;

    #region DragEnded
    public static readonly RoutedEvent DragEndedEvent =
        EventManager.RegisterRoutedEvent("DragEnded", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DragDropRowBehavior));
    public static void AddDragEndedHandler(DependencyObject d, RoutedEventHandler handler)
    {
        UIElement uie = d as UIElement;
        if (uie != null)
            uie.AddHandler(DragDropRowBehavior.DragEndedEvent, handler);
    }
    public static void RemoveDragEndedHandler(DependencyObject d, RoutedEventHandler handler)
    {
        UIElement uie = d as UIElement;
        if (uie != null)
            uie.RemoveHandler(DragDropRowBehavior.DragEndedEvent, handler);
    }

    private void RaiseDragEndedEvent()
    {
        var args = new RoutedEventArgs(DragDropRowBehavior.DragEndedEvent);
        AssociatedObject.RaiseEvent(args);
    }
    #endregion

    #region Popup
    public static readonly DependencyProperty PopupProperty =
        DependencyProperty.Register("Popup", typeof(System.Windows.Controls.Primitives.Popup), typeof(DragDropRowBehavior));
    public System.Windows.Controls.Primitives.Popup Popup
    {
        get { return (System.Windows.Controls.Primitives.Popup)GetValue(PopupProperty); }
        set { SetValue(PopupProperty, value); }
    }
    #endregion

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.BeginningEdit += OnBeginEdit;
        AssociatedObject.CellEditEnding += OnEndEdit;
        AssociatedObject.MouseLeftButtonUp += OnMouseLeftButtonUp;
        AssociatedObject.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
        AssociatedObject.MouseMove += OnMouseMove;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.BeginningEdit -= OnBeginEdit;
        AssociatedObject.CellEditEnding -= OnEndEdit;
        AssociatedObject.MouseLeftButtonUp -= OnMouseLeftButtonUp;
        AssociatedObject.MouseLeftButtonDown -= OnMouseLeftButtonDown;
        AssociatedObject.MouseMove -= OnMouseMove;

        Popup = null;
        draggedItem = null;
        isEditing = false;
        isDragging = false;
    }

    private void OnBeginEdit(object sender, DataGridBeginningEditEventArgs e)
    {
        isEditing = true;
        //in case we are in the middle of a drag/drop operation, cancel it...
        if (isDragging) ResetDragDrop();
    }

    private void OnEndEdit(object sender, DataGridCellEditEndingEventArgs e)
    {
        isEditing = false;
    }

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (isEditing) return;

        var row = UIHelpers.TryFindFromPoint<DataGridRow>((UIElement)sender, e.GetPosition(AssociatedObject));
        if (row == null || row.IsEditing) return;

        //set flag that indicates we're capturing mouse movements
        isDragging = true;
        draggedItem = row.Item;
    }

    private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (!isDragging || isEditing)
            return;

        //get the target item
        var targetItem = AssociatedObject.SelectedItem;

        if (targetItem == null || !ReferenceEquals(draggedItem, targetItem))
        {
            //get target index
            var targetIndex = ((AssociatedObject).ItemsSource as IList).IndexOf(targetItem);

            //remove the source from the list
            ((AssociatedObject).ItemsSource as IList).Remove(draggedItem);

            //move source at the target's location
            ((AssociatedObject).ItemsSource as IList).Insert(targetIndex, draggedItem);

            //select the dropped item
            AssociatedObject.SelectedItem = draggedItem;
            RaiseDragEndedEvent();
        }

        //reset
        ResetDragDrop();
    }

    private void ResetDragDrop()
    {
        isDragging = false;
        Popup.IsOpen = false;
        AssociatedObject.IsReadOnly = false;
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (!isDragging || e.LeftButton != MouseButtonState.Pressed)
            return;

        Popup.DataContext = draggedItem;
        //display the popup if it hasn't been opened yet
        if (!Popup.IsOpen)
        {
            //switch to read-only mode
            AssociatedObject.IsReadOnly = true;

            //make sure the popup is visible
            Popup.IsOpen = true;
        }

        var popupSize = new Size(Popup.ActualWidth, Popup.ActualHeight);
        Popup.PlacementRectangle = new Rect(e.GetPosition(AssociatedObject), popupSize);

        //make sure the row under the grid is being selected
        var position = e.GetPosition(AssociatedObject);
        var row = UIHelpers.TryFindFromPoint<DataGridRow>(AssociatedObject, position);
        if (row != null) AssociatedObject.SelectedItem = row.Item;
    }
}

希望它能帮助未来的用户:)