After the Enums the Lazy Way post I was left thinking about this TextValueItem class I created. It seemed to have much more potential. So I began to think of the following piece of code I stumble upon daily:
cbPerson.DisplayMember = "Name";
cbPerson.ValueMember = "Id";
cbPerson.DataSource = persons;
cbPerson.DataBind(); //On Web Scenarios
We have to bind all sorts of different entities and objects to Checklists, listboxes, comboboxes and the like on Windows and Web applications. So I figured I could use the TextValueItem class and do some nice things to save time, effort and to increase code readability. The solution is to create an attribute and some extensions to easily bind a List to ListControls either on web or windows apps.
The Solution
The solution is quite straightforward. I just created a TextValueAttribute that is applied to your entities/classes. Then I created a few extension methods to provide syntactic sugar for usage scenarios. In the end all IEnumerable<T> objects when T has the TextValue attribute, could be converted to a List<TextValueItem> and then be bound to any control that inherits from ListControl (for both System.Windows and System.Web namespaces).
Here’s the Attribute Code:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class TextValueAttribute : Attribute
{
public TextValueAttribute(string textProperty)
: this(textProperty, "Id")
{
}
public TextValueAttribute(string textProperty, string valueProperty)
{
TextProperty = textProperty;
ValueProperty = valueProperty;
}
public string TextProperty { get; private set; }
public string ValueProperty { get; private set; }
}
Here we simply define which properties of a given class will serve as Text and Value providers for binding to ListControls. The Usage is quite simple as ilustrated below.
[TextValue("Name")] //This asumes "Id" as the value property, as is the case in this class.
public class Person
{
public Person(int id, string name)
{
Name = name;
Id = id;
}
public string Name { get; set; }
public int Id { get; set; }
}
Then I created a class containing a few extension methods for IEnumerable<T> and for List<TextValueItem>. These will enable all our classes decorated with TextValueAttribute to be binded really easy.
Here’s the code for all extensions:
public static class TextValueItemExtensions
{
public static List<TextValueItem> GetAsTextValueItems<TEntity>(this IEnumerable<TEntity> list, string defaultText, string defaultValue)
{
List<TextValueItem> items = GetAsTextValueItems(list);
items.Insert(0,new TextValueItem(defaultText,defaultValue));
return items;
}
public static List<TextValueItem> GetAsTextValueItems<TEntity>(this IEnumerable<TEntity> list)
{
//Look for TextValueAttribute in type used in IEnurable.
Type type = typeof(TEntity);
object[] attributes = type.GetCustomAttributes(typeof(TextValueAttribute), true);
if (attributes.Length == 0)
{
throw new ArgumentException(string.Format("Type '{0}' doesn't have the TextValue Attribute.", type.FullName), "list");
}
//Retrieve Information for binding.
var attribute = attributes[0] as TextValueAttribute;
PropertyInfo TextProperty = type.GetProperty(attribute.TextProperty);
if (TextProperty == null)
{
throw new ArgumentException(string.Format("Type '{0}' has an invalid property name '{1}' for the Text field in the TextValue Attribute.", type.FullName, attribute.TextProperty), "list");
}
PropertyInfo ValueProperty = type.GetProperty(attribute.ValueProperty);
if (ValueProperty == null)
{
throw new ArgumentException(string.Format("Type '{0}' has an invalid property name '{1}' for the Value field in the TextValue Attribute.", type.FullName, attribute.ValueProperty), "list");
}
//Return instances.
List<TextValueItem> items = new List<TextValueItem>();
foreach (var item in list)
{
items.Add(new TextValueItem(TextProperty.GetValue(item, null).ToString(), ValueProperty.GetValue(item, null).ToString()));
}
return items;
}
public static void BindTo(this List<TextValueItem> list, ListControl listControl)
{
listControl.DisplayMember = "Text";
listControl.ValueMember = "Value";
listControl.DataSource = list;
}
public static void BindTo(this List<TextValueItem> list, System.Web.UI.WebControls.ListControl listControl)
{
listControl.DataTextField = "Text";
listControl.DataValueField = "Value";
listControl.DataSource = list;
listControl.DataBind();
}
}
These extension methods have a caveat, they have a dependency on both the System.Windows.Forms and System.Web assemblies, I cringed myself when I saw this, but flexibility comes at a price. In any case if your project is only Windows o Web, you could easily remove the extension methods for the type of application you’re not using.
Usage
So the usage is the same for both windows and web, you’d just do this:
List<Person> persons = new List<Person>();
persons.Add(new Person(1, "Héctor"));
persons.Add(new Person(2, "David"));
persons.Add(new Person(3, "Charles"));
persons.Add(new Person(4, "Joan"));
//Get All Persons
persons.GetAsTextValueItems().BindTo(lbPerson);
//Get All Persons and add new Item as default.
persons.GetAsTextValueItems("[Select]", "0").BindTo(cbPerson);
And that’s it
….I hope it’s usefull to you!








