Launching a custom Dialog to edit items in a DataGrid (with MVVM)

Launching a custom dialog for editing on the DataGrid is another somewhat common request that I see from the discussion list. I thought I would provide a sample but at the same time implement it with the MVVM pattern. The requirement that I will use on the sample is that the editing mechanism can only come from the editing dialog that I provide.

I decided that the way to launch the custom dialog will be through double-clicking on the row. To get that behavior, I will use the attached behavior pattern on DataGridRow. The behavior is defined like so:

public class DataGridRowBehavior

{

  public static DependencyProperty OnDoubleClickProperty = DependencyProperty.RegisterAttached(

      "OnDoubleClick",

      typeof(ICommand),

      typeof(DataGridRowBehavior),

      new UIPropertyMetadata(DataGridRowBehavior.OnDoubleClick));

  public static void SetOnDoubleClick(DependencyObject target, ICommand value)

  {

      target.SetValue(DataGridRowBehavior.OnDoubleClickProperty, value);

  }

  private static void OnDoubleClick(DependencyObject target, DependencyPropertyChangedEventArgs e)

  {

      var element = target as DataGridRow;

      if ((e.NewValue != null) && (e.OldValue == null))

      {

        element.PreviewMouseDoubleClick += OnPreviewMouseDoubleClick;

      }

      else if ((e.NewValue == null) && (e.OldValue != null))

      {

        element.PreviewMouseDoubleClick -= OnPreviewMouseDoubleClick;

      }

  }

  private static void OnPreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)

  {

      UIElement element = (UIElement)sender;

      ICommand command = (ICommand)element.GetValue(DataGridRowBehavior.OnDoubleClickProperty);

      command.Execute(e);

  }

}

 

The behavior is then used on the DataGridRow like this:           

<Style TargetType="{x:Type toolkit:DataGridRow}">

  <Setter Property="local:DataGridRowBehavior.OnDoubleClick"

          Value="{Binding ShowEditItemDialogCommand}" />

</Style>

 

What’s happening is the OnDoubleClick attached property listens for a property change on itself. I define an ICommand, ShowEditItemDialogCommand, on OnDoubleClick which then hooks up the actual event handler, PreviewMouseDoubleClick, on the target element. In that event handler, OnPreviewMouseDoubleClick, I retrieve the value of OnDoubleClick which is my ICommand and execute it. So now we have the behavior to launch a dialog through a double-click action. The next thing is to define the ICommand ShowEditItemDialogCommand.

I have defined a DataGridRow ViewModel which hosts the actual item data as well as any properties that will drive the view (the DataGridRow). It also will host any commands on a DataGridRow which for our purposes will be the ShowEditItemDialogCommand. My commands are also implemented using the RelayCommand mechanism that is discussed on this MSDN article. So here is the basic implementation:                           

public class ItemViewModel : ViewModelBase

{

  private ICommand _editItemCommand;

  public ICommand ShowEditItemDialogCommand

  {

  get

{

      if (_editItemCommand == null)

{

        _editItemCommand = new RelayCommand(

        () =>

           {

                 // pass the item to the dialog

                 var dlg = new EditItemDialog(Item);

                 if (dlg.ShowDialog() == true)

                 {

                 // retrieve the updated item from the dialog

                 Item = dlg.Result.Item;

                 }

           });

      }

      return _editItemCommand;
}

  }

}

 

So when DataGridRowBehavior.OnPreviewMouseDoubleClick executes the command, my ShowEditItemDialogCommand will initiate the launch of the new dialog. The dialog that is being launched is basically a new Window which is considered a View. From a MVVM purist perspective this does not belong in a ViewModel. To handle this you can use the mediator design pattern to host the communication between the ViewModel and the View. There are a couple ways to implement this pattern which include implementing it as an observer pattern or using a specialized communication mechanism through interfaces and references. I’ve chosen the latter and in the sample code you can see I’ve named it EditDialogDirector to be clear on its behavior. I won’t show all the details here.

Now, I said earlier that I set a requirement that editing can only come from the dialog that I provide. While there are a lot of ways to customize the editing behavior, I decided to keep it really simple and set DataGrid.IsReadOnly to true. It does mean that I will lose the default editing capabilities that DataGrid provides, but I won’t have to keep state on DataGrid begin and commit edits. There are some other details that I’m leaving out but the full sample code below will give you a good idea of how everything is pieced together. Also, there is a lot of great reference material and samples on MVVM to further enhance your knowledge on the subject.

https://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx

https://karlshifflett.wordpress.com/mvvm/

https://www.codeproject.com/KB/WPF/InternationalizedWizard.aspx

Download the full sample here.

 

 

DataGridMVVMEditingSample.zip