Automatic Lookup Columns in Grids

A common scenario in data binding is for data classes to contain references to other data classes. For example, a Product object may contain references to Category objects.

In ADO.NET, the references usually appear as foreign keys that map in other tables (e.g., Product.CategoryID).

In the Entity Framework and RIA Services, you still get the key columns, but you also get the actual objects. So you have Product.CategoryID (usually an integer) and Product.Category (an actual Category object).

Displaying foreign keys in a grid is not very useful, because it is unlikely that users will remember that category 12 is "Dairy Products". Allowing users to edit these keys would be even worse. A common way to work around this problem is to remove the related entity columns from any bound grids and, optionally, to replace them with custom columns that use combo boxes for editing the values, so called lookups. The combo boxes have to be bound to the related tables and have their properties set so they display relevant values (e.g., Category.Name) and so they are synchronized with the values being displayed on the grid. This is not terribly hard to do, but it is a tedious and error-prone task that makes projects harder to create and maintain.

C1DataSource can do this tedious work for the developer; it can automatically change related entity columns so that they show combo box lookups. It can do this for several types of data grids, those that it supports. Currently supported Silverlight grids are: C1FlexGrid and C1DataGrid. Here we will show how to do this for C1FlexGrid.

C1DataSource provides an attached property called ControlHandler. If you place a C1FlexGrid control on a designer surface that contains a C1DataSource, you can set an additional C1DataSource.ControlHandler property on the grid. A ControlHandler is an object containing (at present) a single boolean property AutoLookup. This property set to True causes the C1DataSource to configure grid columns that contain references to other entities so that they show lookup combos.

To see how it works, follow these steps:

1.   Using the project used in Simple Binding, add a new form with a C1DataSource control using the same ObjectContextType as before.

2.   Create a ViewSource in the ViewSourceCollection editor, entering Products as the EntitySetName.

3.   Add a C1FlexGrid to the designer and bind it to Products as we did it before:

ItemsSource="{Binding[Products], ElementName=c1DataSource1}"

 

4.   Save, build and run the application. It will look like this:

 

 

As you can see, the Category column is not very useful. You could remove it or customize the grid by writing some code to create a new column, but there’s an easier way.

5.   Add the following inside the C1FlexGrid markup in XAML (the added markup is shaded):

<c1:C1FlexGrid AutoGenerateColumns="True" Name="dataGrid1"
      ItemsSource
="{Binding GetProducts], ElementName=c1DataSource1}" >
          <c1:C1DataSource.ControHandler>
                 <c1:ControlHandler AutoLookup="True"/>
          </c1:C1DataSource.ControHandler>
   </c1:C1FlexGrid>

6.   Run the project again and look at the Category column. Notice that now the grid shows the category name instead of the cryptic strings. Also notice that you can edit the product’s category by picking from a drop-down list, complete with auto search functionality.

 

 

7.   Finally, click the column header to sort the grid by Category and notice that the sorting is performed based on the value displayed on the grid. This is what anyone would expect, but surprisingly it is not easy to achieve using regular data binding.

The string value (name) shown by the combo box is determined following these rules:

1.   If the entity class overrides the ToString method, then the string representation of the entity is obtained using the overridden ToString method. This should return a string that uniquely represents the entity. For example, it could be the content of a CompanyName column, or a combination of FirstName, LastName, and EmployeeID. This is the preferred method because it is simple, flexible, and easy to implement using partial classes (so your implementation will not be affected if the entity model is regenerated).

2.   If the entity class does not override the ToString method, but one of its properties has the DefaultProperty attribute, then that property is used as the string representation of the entity.

3.   If the entity class does not override the ToString method and has no properties marked with the DefaultProperty attribute, then the first column that contains the string "Name" or "Description" in its name will be used as a string representation for the entities.

4.   If none of the above applies, then no lookup is created for the given entity type.


Send us comments about this topic.
Copyright © GrapeCity, inc. All rights reserved.