I came up with a solution today to a small little problem. The problem is when I'm data binding from a collection of objects to a list control.
myListControl.DataSource = myCollectionOfObjects;
myListControl.DataTextField = "Name";
myListControl.DataValueField = "ID";
myListControl.DataBind();
As you may (or may not know) what this will do is take myCollectionOfObjects, populates myListControl using the objects in that collection by making the ListItems value property equal to Name property on the object in the collection and the Text value property equal to the ID of the object.
Assuming my object is defined as so:
public class MyObject
{
public int ID { get; set; }
public string Name { get; set; }
}
and assuming the objects in myCollectionOfObjects have the following data:
| ID | Name |
| 1 | My Object 1 |
| 2 | My Object 2 |
| 3 | My Object 3 |
| 4 | My Object 4 |
I'd end up with this:
Which is all well and good. But the problem is back up with the C# code, I tell it which properties of my class I'm using for data binding via strings. This means there is no compile-time checking against those names and if those names change I won't know until runtime, when my lovely web-app crashes and burns spectacularly with a cryptic error message.
So I set about to resolve this, and I did, thanks to the wonders of extension methods, lamdas and expression trees.
I wrote the following extension class for ListControls:
public static class ListControlExtensions
{
public static void SetDataBindForList(
this ListControl control, IEnumerable dataSource, Expression> textField, Expression> valueField)
{
control.DataSource = dataSource;
SetDataTextField(control, textField);
SetDataValueField(control, valueField);
}
public static void SetDataTextField(this ListControl control, Expression> textField)
{
var memberExpression = textField.Body as MemberExpression;
if (IsProperty(memberExpression))
{
control.DataTextField = memberExpression.Member.Name;
}
else
throw new ArgumentException("textField is not a property", "textField");
}
public static void SetDataValueField(this ListControl control, Expression> valueField)
{
var memberExpression = valueField.Body as MemberExpression;
if (IsProperty(memberExpression))
{
control.DataValueField = memberExpression.Member.Name;
}
else
throw new ArgumentException("valueField is not a property", "valueField");
}
private static bool IsProperty(MemberExpression memberExpression)
{
if (memberExpression == null ||
memberExpression.Member.MemberType != System.Reflection.MemberTypes.Property)
return false;
return true;
}
}
Here I have three extension methods (and one private helper method) where the first method, SetDataBindForList, is used to setup the data binding and calls the other two to set the DataTextField and DataValueField respectively.
SetDataTextField is used to set the DataTextField if you want to do it by itself and likewise with SetDataValueField except it's for DataValueField.
The new code I write when I want to databind to a ListControl now is this:
myListControl.SetDataBindForList(
myCollectionOfObjects,
o => o.Name,
o => o.ID);
Thanks to the type inference of the compiler this becomes one very simple call and it's checked at compile time for correctness on my field names. Pretty neat huh?
-- Edit: It appears my code plugin for blogengine.net is a bit silly with the whole formatting thing. I'll sort it out this evening.
-- Edit 2: Tidy complete :)