Using IEditableCollectionView with dynamically generated GridViewColumns

I recently got a question on how to implement IEditableCollectionView with GridViewColumns that are dynamically created. Creating the columns dynamically actually doesn’t really have a big impact on IEditableCollectionView implementation. I have a sample below that dynamically adds columns but really does not differ all that much from the first sample.

Side note:

There are some design issues to think about when you want to design with an IEditableCollectionView. If you recall from the first sample I used, I relied on the template (TextBlock for non-editable, TextBox for editable) to control the user from being able to edit an item. Since a TextBox has two-way binding by default, it was actually changing the data source when in edit mode. Implementing IEditableObject and keeping a copy was my mechanism to revert changes if the user wanted to cancel the operation. There is actually another (more optimal) solution that leverages another new feature, but I’m going to have to leave that for a separate post. The new feature is actually briefly mentioned on Jossef’s post for new perf improvements in WPF 3.5 SP1 under Controls Improvements. You can probably already guess what it is, but I won’t say anything for now.

Back to the task at hand:

I want to create columns dynamically and use IEditableCollectionView. I decided to create the column templates in code and create columns for each public property of the data source. The default template is a TextBlock that binds to the particular property of the data source.     

// automatically creates GridViewColumns based on the public properties

private void GenerateGridView(Type type)

{

  GridView gridView = new GridView();

  foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))

  {

    GridViewColumn column = new GridViewColumn();

    // set the header

    column.Header = pi.Name;

    // the default cell template

    FrameworkElementFactory cellTemplateFactory = new FrameworkElementFactory(typeof(TextBlock));

    Binding binding = new Binding(pi.Name);

    cellTemplateFactory.SetBinding(TextBlock.TextProperty, binding);

    DataTemplate defaultCellTemplate = new DataTemplate();

    defaultCellTemplate.VisualTree = cellTemplateFactory;

    column.CellTemplate = defaultCellTemplate;

    gridView.Columns.Add(column);

  }

  this.itemsList.View = gridView;

}

 

Before when I wanted to make an item editable, I changed the ListBoxItem template to a TextBox. Since I’m using columns now, there are some changes to the visual tree being used, and I cannot just change the whole ListBoxItem to a TextBox. Oh, and now that I’m using ListView, they are ListViewItems now and not ListBoxItems.

When using a GridView as the View, the ListViewItem uses a GridViewRowPresenter which will contain ContentPresenters for each column. Now each time I have to update the item’s template, I’m going to drill down into the ListViewItem and update the cell template of each ContentPresenter to be a TextBox.

private void UpdateContentTemplate(bool isEditing, ListViewItem lvi)

{

  // get the content presenter of the particular column
  GridViewRowPresenter rowPresenter = this.GetVisualChild<GridViewRowPresenter>(lvi);

  // go through each column and update the content template

  int columnIndex = 0;

  foreach (GridViewColumn column in rowPresenter.Columns)

  {

    // get the content presenter of the particular column
  ContentPresenter cp = this.GetVisualChild<ContentPresenter>(rowPresenter, columnIndex);

    DataTemplate cellTemplate = new DataTemplate();

    FrameworkElementFactory cellTemplateFactory;

    // if editing, set the template to a textbox, else to a textblock

    if (isEditing)

    {
  cellTemplateFactory = new FrameworkElementFactory(typeof(TextBox));

      Binding binding = new Binding(column.Header.ToString());

      binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

      cellTemplateFactory.SetBinding(TextBox.TextProperty, binding);
}

    else

    {
  cellTemplateFactory = new FrameworkElementFactory(typeof(TextBlock));

      Binding binding = new Binding(column.Header.ToString());

      cellTemplateFactory.SetBinding(TextBlock.TextProperty, binding);
}

    cellTemplate.VisualTree = cellTemplateFactory;

    cp.ContentTemplate = cellTemplate;

    columnIndex++;
}
}

 

The rest of the code is basically the same as in the first sample (with some refactoring). Adding/Removing/Editing all work the same and there are no changes to the data source. For kicks I added commands in addition to the buttons since I was getting tired of having to mouse down to the buttons each time I wanted to edit and submit. The commands are, F2 = Edit, Enter = Submit, Esc = Cancel. Note that this isn’t the most efficient but hopefully it will give you some ideas to expand on. Here is the project.

IEditableCollectionViewSample2.zip