WPF DataGrid: Working with DataGridComboBoxColumns CTP (Part 2)

UPDATE: DataGridComboBoxColumn has been updated from CTP to V1. See the post here for the updates to the DataGridComboBoxColumn as well as an updated sample.

Previously I started a post on DataGridComboBoxColumns where I introduced the APIs specific to it and showed an example of how to use it. Well, one thing I didn't show that seems to be a common ask is how to mask what is displayed versus what is actually being updated. Let’s say I have a CustomerID that I want to display as before with a DataGridComboBoxColumn but I want the display values to be a little more user friendly. For the ComboBox (editing state) I can use DisplayMemberBinding for the display value and SelectedValuePath for the actual value but for the TextBlock (non-editing state) I don't have anything that can map the CustomerID to something else. Let’s take a look at a couple solutions.

One solution could be using a converter on the DataFieldBinding to transform the CustomerID to some other alias. Remember though in our example, the overall table (Orders) that the DataGrid is displaying is different that the table that the ComboBox is using (Customers). So in your converter you may need to optimize the calls to get the Customer table. I have excluded that from the example as that is a separate topic of discussion.            

<dg:DataGridComboBoxColumn

        DataFieldBinding="{Binding CustomerID,

                           Converter={StaticResource CustomerConverter},

                         ConverterParameter=ContactName}"

        ItemsSource="{Binding Source={StaticResource customerDataProvider}}"

        Header="CustomerID (ContactName alias)"

        DataFieldTarget="Text">

  <dg:DataGridComboBoxColumn.EditingElementStyle>

    <Style TargetType="ComboBox">

      <Setter Property="SelectedValuePath" Value="CustomerID" />

      <Setter Property="DisplayMemberPath" Value="ContactName" />

    </Style>

  </dg:DataGridComboBoxColumn.EditingElementStyle>

</dg:DataGridComboBoxColumn>

           

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

{

  string customerID = (string)value;

  DataTable dataTable = DBAccess.GetCustomers().Tables["Customers"];

  foreach (DataRow row in dataTable.Rows)

  {

    if (row["CustomerID"].ToString() == customerID)

      return row[parameter.ToString()];

  }

  return null;

}

The converter is returning a string value and remember that the DataFieldTarget is what maps the ComboBox value back to the DataFieldBinding, so I have set the DataFieldTarget to “Text”. Also, as the DisplayMemberPath and ConverterParameter show, I am using the ContactName as the alias.

Another solution which is very similar to the first one is to use the converter as I did above but with a DataGridTemplateColumn. There really isn’t a big advantage going with a DataGridTemplateColumn over a DataGridComboBoxColumn in this particular example, but it does give you more flexibility on the type of element to display in the non-editing state. Anyway, here is an example:                       

<dg:DataGridTemplateColumn Header="CustomerID (ContactName alias)">

  <dg:DataGridTemplateColumn.CellTemplate>

    <DataTemplate>

      <TextBlock Text="{Binding Path=CustomerID, Mode=OneWay, Converter={StaticResource CustomerConverter}, ConverterParameter=ContactName}" />

    </DataTemplate>

  </dg:DataGridTemplateColumn.CellTemplate>

  <dg:DataGridTemplateColumn.CellEditingTemplate>

    <DataTemplate>

      <ComboBox ItemsSource="{Binding Source={StaticResource customerDataProvider}}"

        SelectedValue="{Binding CustomerID}" SelectedValuePath="CustomerID" DisplayMemberPath="ContactName" />

    </DataTemplate>

  </dg:DataGridTemplateColumn.CellEditingTemplate>

</dg:DataGridTemplateColumn>

One thing to note about this example is what is bound to what. In CellTemplate, the TextBlock is using DataGrid’s DataContext and is bound to the CustoemrID field of the Orders table. In CellEditingTemplate, the ComboBox is using the Customer table as the Source for the ItemsSource but the DataContext is still the DataGrid for everything elese. That means that SelectedValue’s binding to CustomerID is based on the CustomerID of the Orders table and that binding is the one that will update the source.

A third and a little more time consuming approach would be to subclass DataGridComboBoxColumn and add a property for the display. If you take a look at the DataGridHyperlinkColumn it actually has a special API for something like this which is ContentBinding. ContentBinding maps the content it is bound to what gets displayed as the link in the cells of that column. However, there is still a DataFieldBinding which maps to the actual hyperlink that it represents.

Here is a sample for the first two solutions.

 

DataGrid_ComboBoxColumnSamples.zip