title: Displaying/Editing complex objects in the WPF Toolkit DataGrid using MVVM
body: <p>I have a collection of complex models each containing a collection of interface instances to other complex models and I need to display these parent and child complex models, allowing all properties of the parent and child complex models to be edited. </p>
  
  <p>How can I best display this data and allow editing of the parent and child object's properties individually as well as through a combination of the selection of multiple cells and a context menu click (i.e. change the same property value on a child model across multiple parents)? I also need to be able to perform actions like setting model property values to some other complex model instance via search from within the editing mechanism (DataGrid cell currently)? </p>
  
  <p>Below is a generic example of classes that approximates what I am working with in the application.</p>
  
  <pre><code>enum ChildType 
  { 
      One,
      Two,
      Three
  }
  
  class ComplexType
  {
      public long ID { get; set; }
      public string Name { get; set; }
  
      public override string ToString()
      { 
          return Name;
      }
  }        
  
  class IChildModel
  {
      ChildType Type { get; set; }
      string Name { get; set; }
  }
  
  class ChildModel1 : IChildModel
  {
      public ChildType Type { get; set; }
      public string Name { get; set; }
      public string Property1 { get; set; }
      public decimal Property2 { get; set; }
      public ComplexType Property3 { get; set; }
  }
  
  class ChildModel2 : IChildModel
  {
      public ChildType Type { get; set; }
      public long Property1 { get; set; }
      public string Property2 { get; set; }
  }
  
  class Parent
  {
      public long ID { get; set; }
      public string Name { get; set; }
      public CustomObservableCollection&lt;IChildModel&gt; Children { get; set; }
  }
  
  class ViewModel
  {
      public CustomObservableCollection&lt;Parent&gt; Parents { get; set; }
  }
  </code></pre>
  
  <p>Thus far I have implemented the application using a DataGrid and dynamically generated the columns in the View code-behind using reflection. The binding of the columns for the child complex object instances uses a subscript on the CustomObservableCollection&lt;> (custom collection allowing indexing by an generic value [enum ChildType] in this case). The binding in particular has made it difficult to properly set a value on a same property across multiple parent's child instances (via multi-select on a column and a context menu click to set a value). Again, I am handling these sort of mass changes in the code-behind on the View, using reflection a binding path parsing to set the property values (it feels wrong; hate doing it that way). I would like to be able to set the selected children on the ViewModel and pass the property name and new value for the property to a command in the ViewModel to make the changes. Even being able to pass the command the child type, property and new value would be nice (I think). </p>
  
  <p>My research through Google, stackoverflow, Code Project, etc. has pointed me toward my current solution but I feel I am thinking about the problem incorrectly and there should be a better MVVM approach to this.</p>
  
  <p><strong>EDIT</strong></p>
  
  <p>The primary focus for this application is to allow the editing of multiple parent and child model instances in a view where the user can compare values of several instances and be allowed to set the value of a parent or child property across multiple objects of the same type to the same value (i.e. Parent1 and Parent2 both have a ChildModel1 and user wants to set the Name on Property3 of both parent objects' ChildModel1 to "X"). Although, the application still must allow individual edits of properties on parent and child objects (DataGrid does seem to fill the requirement nicely). In meeting these requirements, I implemented dynamic column creation in the view. Below is a generic example of what this logic looks like.</p>
  
  <pre><code>private void DataGrid_TargetUpdated(object sender, DataTransferEventArgs e)
  {
      var vm = DataContext as ViewModel;
  
      if (vm != null &amp;&amp; vm.Parents != null) {
          List&lt;ChildType&gt; processedChildTypes = new List&lt;ChildType&gt;();
  
          foreach (var parent in vm.Parents) {
              for (int childIndex = 0; childIndex &lt; parent.Children.Count; ++childIndex) {
                  var child = vm.Children[childIndex];
  
                  if (!processedChildTypes.Contains(child.Type)) {    // Ensure each child type is only processed once
                      processedChildTypes.Add(child.Type);
                      CreateChildPropertyColumns(processedChildTypes, child);
                  }
          }
      }
  }
  
  private void CreateChildPropertyColumns(List&lt;ChildType&gt; processedChildTypes, IChildModel child)
  {
      PropertyInfo[] childProperties = child.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); // Only use properties declared on the child type
      Type childInterfaceType = typeof(IChildModel);
  
      foreach (PropertyInfo childProperty in childProperties) {   
          // Only create a column if the property is editable
          if (childProperty.CanWrite) {
              if (childInterfaceType.IsAssignableFrom(childProperty.PropertyType)) {
                  var subChild = childProperty.GetValue(child, null) as IChildModel;
  
                  if (subChild != null &amp;&amp; !processedChildTypes.Contains(subChild.Type)) {
                      processedChildTypes.Add(subChild.Type);
                      CreateChildPropertyColumns(processedChildTypes, subChild);
                  }
              }
              else
                  dataGrid.Columns.Add(CreateChildPropertyColumn(child.Type, childProperty));
          }
      }
  }
  
  private DataGridColumn CreateChildPropertyColumn(ChildType childType, PropertyInfo propertyInfo)
  {
      DataGridColumn column = null;
      var binding = new Binding(string.Format("Children[{0}].{1}", childType, propertyInfo.Name));
  
      /* Create column based on PropertyInfo here */
      /* Default case is a text column */
      column = new DataGridTextColumn() { Binding = binding };
      column.Header = propertyInfo.Name;
  
      return column;
  }
  </code></pre>