Data-binding hierarchical objects

After my post about my ObjectField column, I thought I’d elaborate a bit on why it’s necessary.

When you’re data binding against an object that isn’t flat (i.e. has properties that are more than simple types - namely classes), you are bound to encounter the following exception, which is caused by the BoundField incorrectly handling a hierarchical object path.

A field or property with the name MySubObject.PropertyName was not found on the selected data source.

Take this following Customer object for example:

public class Customer
{
	...

	public ContactDetails ContactDetails
	{
		get { return contactDetails; }
	}
}

public class ContactDetails
{
	...

	public string TelephoneNumber
	{
		get { return telephoneNumber; }
	}
}

If you were to just use DataField="ContactDetails" on a BoundField, it would work fine because it’s binding against a property on your customer. However, if you were to try to get the TelephoneNumber property of the ContactDetails by doing: DataField="ContactDetails.TelephoneNumber", it would fail because the field can’t interpret the two property names; it treats the DataField as one big name, which obviously isn’t correct.

The BoundField simply isn’t capable of resolving this kind of hierarchical path using late-binding. This is because it uses the DataField as the literal property name on the component, using a TypeDescriptor to get the property.

TypeDescriptor.GetProperties(component).Find(dataField, true);

This strikes me as a bit odd to be honest, because the DataBinder has the ability to evaluate a hierarchical path. It’s pure speculation, but if this is a conscious decision it may be down to the performance implications of using late binding; however, I can’t see that it’s any worse than reflection.

Unfortunately there isn’t a solution to this if you still want to use the BoundField. If you don’t mind a bit of untidy mark-up, you can do this instead:

<asp:TemplateField>
  <ItemTemplate>
    <%# Eval("ContactDetails.TelephoneNumber") %>
  </ItemTemplate>
</asp:TemplateField>

This is pretty messy though, and you’re going to quadruple the markup for your columns; imagine having 10 of those, it’s going to get pretty ugly. My solution is to use the ObjectField I wrote about previously, which is a column that derives from BoundField and overrides it’s binding mechanism, allowing it to correctly evaluate hierarchical paths.

The ObjectField allows you to use the familiar markup from the BoundField:

<jag:ObjectField BoundField="ContactDetails.TelephoneNumber" />

Hopefully one of those solutions will suit you. Personally I’d prefer to see the ObjectField, or other derived field, instead of the nasty TemplateField usage.

This is a follow up to my ObjectField post, because a few people have been hitting that page in search of the exception, which it doesn’t really cover.