10 July 2012

WPF DataGrid: Enable inline adding of records for view models with non-default constructors/interfaces

If you're using MVVM with interfaces you've certainly seen that the WPF DataGrid doesn't display an empty row at the end and does not allow the user to add new rows to the grid. An example:


interface IPersonViewModel
{
   string FirstName { get; set; }
   string LastName { get; set; }
}

interface IPersonListViewModel
{
    IList<IPersonViewModel> Persons { get; }
}

If you bind IPersonsListViewModel.Persons to a DataGrid you will not see an empty row at the end allowing the user to add new rows. The reason is that the DataGrid does not know how to instantiate IPersonViewModel for the new row (or more correctly the ListCollectionView.)

We've been struggling with this for the last couple of days as we're porting our WinForms app to WPF and are throwing away our custom data binding engine we've used for WinForms for the last three years.

The issue is as I said above that ListCollectionView does not know how to instantiate new rows and thus returns false from its CanAddNew property. We've solved it by creating a custom ListCollectionView which uses a factory function (a delegate) to create new rows. This is the class:

   public class TypedListCollectionView<T> : ListCollectionView, IEditableCollectionView
   {
       private Func<T> factory;

       public TypedListCollectionView(IList collection, Func<T> factory)
           : base(collection)
       {
           this.factory = factory;
       }

       bool IEditableCollectionView.CanAddNew
       {
           get
           {
               return this.CanAddNewItem;
           }
       }

       object IEditableCollectionView.AddNew()
       {
           T obj = this.factory();
           return this.AddNewItem(obj);
       }
   }

The class overrides two interface methods from ListCollectionView. CanAddNew checks if CanAddNewItem is supported by the base class in order to not break the internal logic of ListCollectionView related to editing and new items. The other overload AddNew uses the factory method provided to the constructor to create the new row and passes this to the AddNewItem method of ListCollectionView. This keeps the ListCollectionView internal logic working and enables the DataGrid to add new rows.

1 comment:

Aprikaner said...

Yeah es gibt was zu lesen.....