在WPF DataGrid上拖动Drop Row行为

我正在尝试通过执行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(OnBeginEdit); dataGrid.CellEditEnding += new EventHandler(OnEndEdit); dataGrid.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp); dataGrid.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown); dataGrid.MouseMove += new MouseEventHandler(OnMouseMove); } else { dataGrid.BeginningEdit -= new EventHandler(OnBeginEdit); dataGrid.CellEditEnding -= new EventHandler(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; } ///  /// Initiates a drag action if the grid is not in edit mode. ///  private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (IsEditing) return; var row = UIHelpers.TryFindFromPoint((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; } ///  /// Completes a drag/drop operation. ///  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(); } ///  /// Closes the popup and resets the /// grid to read-enabled mode. ///  private static void ResetDragDrop() { IsDragging = false; popup.IsOpen = false; dataGrid.IsReadOnly = false; } ///  /// Updates the popup's position in case of a drag/drop operation. ///  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(dataGrid, position); if (row != null) dataGrid.SelectedItem = row.Item; } } 

助手class

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

 #region find parent ///  /// Finds a parent of a given item on the visual tree. ///  /// The type of the queried item. /// A direct or indirect child of the /// queried item. /// The first parent item that matches the submitted /// type parameter. If not matching item can be found, a null /// reference is being returned. public static T TryFindParent(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(parentObject); } } ///  /// This method is an alternative to WPF's ///  method, which also /// supports content elements. Do note, that for content element, /// this method falls back to the logical tree of the element. ///  /// The item to be processed. /// The submitted item's parent, if available. Otherwise /// null. 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 ///  /// 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. ///  /// The dependency object that marks a starting /// point. This could be a dialog window or a panel control that /// hosts bound controls. /// The properties to be updated if ///  or one of its childs provide it along /// with a binding expression. 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 ///  /// Tries to locate a given item within the visual tree, /// starting with the dependency object at a given position. ///  /// The type of the element to be found /// on the visual tree of the element at the given location. /// The main element which is used to perform /// hit testing. /// The position to be evaluated on the origin. public static T TryFindFromPoint(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(element); } 

}

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

有办法做到这一点….

我似乎无法找到办法

最后,我得到了问题,并对此进行了一些更改才能正常工作

我用这个例子来制作Drag Drop Logic和使这个行为可能是对其他人的使用….请建议改进​​我会很乐意改变…

行为

 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(OnBeginEdit); dataGrid.CellEditEnding += new EventHandler(OnEndEdit); dataGrid.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(OnMouseLeftButtonUp); dataGrid.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown); dataGrid.MouseMove += new MouseEventHandler(OnMouseMove); } else { dataGrid.BeginningEdit -= new EventHandler(OnBeginEdit); dataGrid.CellEditEnding -= new EventHandler(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; } ///  /// Initiates a drag action if the grid is not in edit mode. ///  private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (IsEditing) return; var row = UIHelpers.TryFindFromPoint((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; } ///  /// Completes a drag/drop operation. ///  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(); } ///  /// Closes the popup and resets the /// grid to read-enabled mode. ///  private static void ResetDragDrop() { IsDragging = false; popup.IsOpen = false; dataGrid.IsReadOnly = false; } ///  /// Updates the popup's position in case of a drag/drop operation. ///  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(dataGrid, position); if (row != null) dataGrid.SelectedItem = row.Item; } } } 

UIHelper类

 public static class UIHelpers { #region find parent ///  /// Finds a parent of a given item on the visual tree. ///  /// The type of the queried item. /// A direct or indirect child of the /// queried item. /// The first parent item that matches the submitted /// type parameter. If not matching item can be found, a null /// reference is being returned. public static T TryFindParent(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(parentObject); } } ///  /// This method is an alternative to WPF's ///  method, which also /// supports content elements. Do note, that for content element, /// this method falls back to the logical tree of the element. ///  /// The item to be processed. /// The submitted item's parent, if available. Otherwise /// null. 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 ///  /// 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. ///  /// The dependency object that marks a starting /// point. This could be a dialog window or a panel control that /// hosts bound controls. /// The properties to be updated if ///  or one of its childs provide it along /// with a binding expression. 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 ///  /// Tries to locate a given item within the visual tree, /// starting with the dependency object at a given position. ///  /// The type of the element to be found /// on the visual tree of the element at the given location. /// The main element which is used to perform /// hit testing. /// The position to be evaluated on the origin. public static T TryFindFromPoint(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(element); } } 

用法

      Text="{Binding Path=Name}" />   

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

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

  public class DragDropRowBehavior : Behavior { 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((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(AssociatedObject, position); if (row != null) AssociatedObject.SelectedItem = row.Item; } } 

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