Drag Drop Row behavior on WPF DataGrid
Asked Answered
T

3

5

I am trying to make an attached behavior to reorder rows bby doing Drag & Drop

I found some solution (On Stackoverflow and by googling) and using them i am trying to make the behavior... I took the example from Hordcodenet website (i dont have the link now)

Code

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

}

Helper Class

/// /// Common UI related helper methods. /// 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);
}

}

Problem is that the Event OnMouseLeftButtonDown is not called when i press it over a row to drag it... but OnMouseLeftButtonUp is called after that....

Is there ne way to do this....

I cant seem to find a way

Tanya answered 8/5, 2012 at 6:11 Comment(1)
oops got the problem ... it should be dataGrid.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);Tanya
T
10

Finally i got the Problem and also made some changes for this to work properly

I used this example to make the Drag Drop Logic amd made this behavior may be its use ful to others .... please suggest improvements i would be happy to change ...

Behavior

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);
    }
}

Usage

    <!--  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 >
Tanya answered 8/5, 2012 at 6:41 Comment(2)
Thank you, this was helpful and saved me time and effort. fwiw, I had to move the comment out of the textblock tag.Mcmanus
will this solution work for dragging multiple rows of datagrid at a time ?Turban
S
2

I've created a behavior out of @Dave solution:

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

Hope it helps the future users :)

Sadism answered 10/10, 2013 at 18:24 Comment(1)
It helped. Added row.DataContext == CollectionView.NewItemPlaceholder check to OnMouseLeftButtonDown to prevent moving the new item placeholder row.Detain
T
1

if the event is not fired at the desired object, it is because some other control is "swallowing" it. Try to use OnPreviewMouseLeftButtonDown instead. If preview is not showing the event, try to move that preview method to a parent class.

Thilda answered 8/5, 2012 at 6:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.