Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Merge Two Rows by Drag and Drop in a WPF Toolkit DataGrid Using MVVM

0.00/5 (No votes)
20 Apr 2011 1  
How to merge two rows by drag-and-drop in DataGrid using MVVM.

Overview

This implementation merges two rows by drag-and-drop in DataGrid using MVVM. The merging logic is implemented in the ViewModel.

1. Introduction

This article shows how to implement drag-and-drop of rows in a DataGrid. The detailed explanation is available in the section below. All the code related to the drag-and-drop functionality of the DataGrid is in the code-behind. To merge two rows in a DataGrid by drag-and-drop, this post tries to put the row merge logic in the ViewModel by using Attached Dependency Properties, which binds the DelegateCommand in the ViewModel to Dependency Properties in the View. The implementation uses the WPF Toolkit DataGrid [3] and the WPF Model-View-ViewModel Toolkit [4].

2. Merge two rows by drag and drop in DataGrid

To handle drag and drop in DataGrid, there are some property bindings in the DataGrid’s XAML:

<WpfToolkit:DataGrid x:Name="listView" RowHeight="30" 
   ItemsSource="{Binding Path=Items}"
   AutoGenerateColumns="False"
   SelectionMode="Single"
   AllowDrop="True"
   MouseMove="OnMainGridMouseMove"
   localControls:MouseMoveInGridSupport.MouseMoveCommand="{Binding MouseMoveCommand}"   
   localControls:MouseDropInGridSupport.MouseDropCommand="{Binding MouseDropCommand}"

The property AllowDrop is set to True, which enables drop in DataGrid. The code behind handler OnMainGridMouseMove handles the event MouseMove, which detects a mouse move in the grid, performs DragDrop, and passes control to other handlers. There is no business logic or data here related to the ViewModel and Model.

private void OnMainGridMouseMove(object sender, MouseEventArgs e)
{
   if (e.LeftButton != MouseButtonState.Pressed)
   {
       e.Handled = true;
       return;
   }

   var row = FindVisualParent<DataGridRow>(e.OriginalSource as FrameworkElement);
   if ((row != null) && row.IsSelected)
   {
       var selectedItem = row.Item;
       var finalDropEffect = DragDrop.DoDragDrop(row, selectedItem, DragDropEffects.Move);
       if (finalDropEffect == DragDropEffects.Move)
       {
          e.Handled = false;
       }
   }
}

There are two Attached Dependency Properties in the DataGrid’s XAML. One is MouseMoveInGridSupport:

public class MouseMoveInGridSupport : ItemSupportBase
{
    public static ICommand GetMouseMoveCommand(DependencyObject obj)
   {
       return (ICommand)obj.GetValue(MouseMoveCommandProperty);
   }

   public static void SetMouseMoveCommand(DependencyObject obj, ICommand value)
   {
       obj.SetValue(MouseMoveCommandProperty, value);
   }

   public static readonly DependencyProperty MouseMoveCommandProperty =
       DependencyProperty.RegisterAttached("MouseMoveCommand", 
       typeof(ICommand), typeof(MouseMoveInGridSupport), 
       new UIPropertyMetadata(null, new PropertyChangedCallback(OnCommandChanged)));

   private static void OnCommandChanged(DependencyObject sender, 
                       DependencyPropertyChangedEventArgs e)
   {
       if (e.OldValue != null)
          ((ItemsControl)sender).MouseMove -= new MouseEventHandler(OnMouseMoveClick);

       if (e.NewValue != null)
       ((ItemsControl)sender).MouseMove += new MouseEventHandler(OnMouseMoveClick);
   }

   private static void OnMouseMoveClick(object sender, MouseEventArgs e)
   {
       DependencyObject source = (DependencyObject)e.OriginalSource;
       var row = TryFindParent<DataGridRow>(source);
       var item = row.Item;
       if (item == null) return;

       var command = GetMouseMoveCommand((DependencyObject)sender);
       if (command != null)
       {
          if (command.CanExecute(item))
             command.Execute(item);
       }
   }
}

MouseMoveInGridSupport detects the MouseMove event happening in the DataGrid, finds the related DataGridRow, and passes its DataItem to the DelegateCommandMouseMoveCommand” in the ViewModel.

The second one is MouseDropInGridSupport:

public class MouseDropInGridSupport : ItemSupportBase
{
   public static ICommand GetMouseDropCommand(DependencyObject obj)
   {
       return (ICommand)obj.GetValue(MouseDropCommandProperty);
   }

   public static void SetMouseDropCommand(DependencyObject obj, ICommand value)
   {
       obj.SetValue(MouseDropCommandProperty, value);
   }

   public static readonly DependencyProperty MouseDropCommandProperty =
       DependencyProperty.RegisterAttached("MouseDropCommand", 
       typeof(ICommand), typeof(MouseDropInGridSupport), 
       new UIPropertyMetadata(null, new PropertyChangedCallback(OnMouseDropCommandChanged)));

   private static void OnMouseDropCommandChanged(DependencyObject sender, 
                       DependencyPropertyChangedEventArgs e)
   {
       if (e.OldValue != null)
          ((ItemsControl)sender).Drop -= new DragEventHandler(OnDropClick);

       if (e.NewValue != null)
          ((ItemsControl)sender).Drop += new DragEventHandler(OnDropClick);
   }

   private static void OnDropClick(object sender, DragEventArgs e)
   {
       DependencyObject source = (DependencyObject)e.OriginalSource;

       var row = TryFindParent<DataGridRow>(source);
       var item = row.Item;

       if (row == null) return;

       var command = GetMouseDropCommand((DependencyObject)sender);
       if (command != null)
       {
          if (command.CanExecute(item))
             command.Execute(item);
       }
   }
}

MouseDropInGridSupport detects the Drop event that happens in the DataGrid, finds the related DataGridRow, and passes its DataItem to the DelegateCommandMouseDropCommand” in the ViewModel.

The logic to merge two rows is in the ViewModel:

public class MainViewModel : ViewModelBase
{
   #region Fields
   private Item dragItem;
   private Item dropItem;
   private DelegateCommand exitCommand;
   public DelegateCommand<Item> MouseMoveCommand { get; private set; }
   public DelegateCommand<Item> MouseDropCommand { get; private set; }
   #endregion

   #region Constructor
   public MainViewModel()
   {
       _items = DataAccess.DataAccess.LoadItems();
       MouseMoveCommand = new DelegateCommand<Item>(MouseMove, CanMouseMove);
      MouseDropCommand = new DelegateCommand<Item>(MouseDrop, CanMouseDrop);
   }
   #endregion

   #region Properties
   IList<Item> _items;
   public IList<Item> Items
   {
       get { return _items; }
       set
       {
          _items = value;
          base.OnPropertyChanged("Items");
       }
   }
   #endregion

   #region Commands
   public ICommand ExitCommand
   {
       get
       {
          if (exitCommand == null)
          {
             exitCommand = new DelegateCommand(Exit);
          }
          return exitCommand;
       }
   } 
   #endregion

   #region Methods
   private void Exit()
   {
       Application.Current.Shutdown();
   }

   private void MouseMove(Item item)
   {
       IList<Item> items = new List<Item>(_items);
       dragItem = item;

       if (dropItem != null && dragItem != null)
       {
          Item newItem = new Item();
          newItem.Name = "Merge row: " + dragItem.Name + " " + dropItem.Name;
          newItem.Data = (dropItem.Data + dragItem.Data) + 100;
          newItem.Id = (dropItem.Id + dragItem.Id) + 100;
          items.Remove(dragItem);
          items.Remove(dropItem);
          items.Add(newItem);
          Items = items;
       }
   }

   private bool CanMouseMove(Item item)
   {
       return true;
   }

   private void MouseDrop(Item item)
   {
       dropItem = item;
   }

   private bool CanMouseDrop(Item item)
   {
       return true;
   }
   #endregion
}

The DelegateCommandMouseDropCommand” gets the drop item. The DelegateCommandMouseDropCommand” gets the move item, then merges the drop and move items.

3. Conclusion

Merging two rows in a grid by drag and drop is implemented in MVVM. It uses Attached Dependency Properties and DelegateCommands to put all the merge logic into the ViewModel.

4. References

  1. Common DataGrid Add-Ons: http://code.msdn.microsoft.com/Common-DataGrid-Add-Ons-4f64fcee
  2. MVVM and the WPF DataGrid: MVVM_DataGrid.aspx
  3. WPF Toolkit DataGrid: http://wpf.codeplex.com/releases/view/40535
  4. WPF Model-View-ViewModel Toolkit: http://wpf.codeplex.com/wikipage?title=WPF%20Model-View-ViewModel%20Toolkit

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here