Entries from February 2008 ↓

ObjectField 1.1

I’ve updated the ObjectField to be considerably simpler than it was before. While writing my Data-binding hierarchical objects post I wrote this about the BoundField implementation:

Using a TypeDescriptor to get the property… This strikes me as a bit odd to be honest, because the DataBinder has the ability to evaluate a hierarchical path.

Which is interesting, because I was using a TypeDescriptor in my ObjectField implementation!

Originally, the ObjectField was using the method below to evaluate the hierarchical paths, which to be honest is a bit verbose.

private object GetNestedValue(object component, string field)
{
	string[] properties = field.Split('.');

	foreach (string property in properties)
	{
		PropertyDescriptor descriptor =
			TypeDescriptor.GetProperties(component).Find(property, true);

		if (descriptor == null && !AllowNulls)
		{
			// no descriptor, and we're not allowing nulls so complain that
			// we can't find the object
			throw new HttpException(string.Format(MissingFieldErrorMessage,
				property));
		}
		else if (descriptor == null)
		{
			// silently return, with the NullValue if present
			component = NullValue;
			break;
		}

		component = descriptor.GetValue(component);
	}

	return component;
}

The GetNestedValue method was splitting the DataField value and then recursively evaluating each property.

Here’s the same implementation using the DataBinder:

// looking to bind against child-objects
object component = DataBinder.GetDataItem(controlContainer);

return DataBinder.Eval(component, DataField);

Magic!

As a side effect of this change, the ObjectField can now support everything regular data-binding does. So you can use indexers and such in your DataField now.

A couple of other things you should know: the AllowNulls property has been removed because it’s no longer supported, and the NullValue field has also been removed because the BoundField already supported it in the form of NullDisplayText.

Downloads

The ObjectField is open-source under the new BSD License; read the license for what you’re allowed to do.

You can download the source here: Download Source.
You can download the latest binary here: Download Binary.

The source is also accessible from Subversion at: http://jagregory.googlecode.com/svn/trunk/ObjectField/ (using user jagregory-read-only)

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.

ObjectField - A GridView field

The version of the ObjectField that this post refers to is now out of date. Please go to the ObjectField 1.1 post for the latest version.

I encountered a problem while binding a complex object to a GridView, in that the BoundField doesn’t support specifying a nested value in it’s DataField property. So if you have a list of Customer’s, and want to display the TelephoneNumber property from inside the customer’s ContactDetails property, you’re out of luck.

<asp:BoundField DataField="ContactDetails.TelephoneNumber" />

The above would fall over with an exception along the lines of:
A field or property with the name 'ContactDetails.TelephoneNumber' was not found on the selected data source.

This is a mind-boggling flaw in the BoundField, with the main solution being to create a nested GridView, which is just overkill for most situations. This problem especially rears it’s ugly head if you’re using an ORM layer such as NHibernate or SubSonic.

So what have I done? I’ve just gone and created a solution to this problem.

Introducing the ObjectField, a GridView field that allows binding against hierarchical structured objects. In short, it takes a BoundField and splits it on full-stops (periods!) using each element to find an object.

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

The above is now possible! Huzzah.

Extras

There’s one extra thing you should know about. The field has a couple of additional properties that can be useful.

The first is AllowNulls which defaults to true, this will make the field return silently when a null is encountered anywhere along the object hierarchy; this can be useful if you know that there might be a null somewhere along the lines.

The second property is NullValue, which is displayed by the field when AllowNulls is true and a null is encountered. Setting this value allows you to give a user-friendly message if a value is null.

Downloads

The ObjectField is open-source under the new BSD License; read the license for what you’re allowed to do.

The version of the ObjectField that this post refers to is now out of date. Please go to the ObjectField 1.1 post for the latest version.