Consolidate Your Binding Code


For this demo, we'll be using a very simple business object:

And a factory class to build our business objects:

Noisy Example:

What we sometimes run across in the UI code-behind is something that looks like this:

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            IEnumerable<BizObject> source = BizObjectFactory.GetAll();

            myCheckBox.DataSource = source;
            myCheckBox.DataTextField = "Value";
            myCheckBox.DataValueField = "Key";
            myCheckBox.DataBind();

            myDropDown.DataSource = source;
            myDropDown.DataTextField = "Value";
            myDropDown.DataValueField = "Key";
            myDropDown.DataBind();

            myBulletedList.DataSource = source;
            myBulletedList.DataTextField = "Value";
            myBulletedList.DataValueField = "Key";
            myBulletedList.DataBind();

            myListBox.DataSource = source;
            myListBox.DataTextField = "Value";
            myListBox.DataValueField = "Key";
            myListBox.DataBind();

            myRadioButtonList.DataSource = source;
            myRadioButtonList.DataTextField = "Value";
            myRadioButtonList.DataValueField = "Key";
            myRadioButtonList.DataBind();
        }
    }
}

The thing that we should be screaming right about now is "Gadzooks! Look at all the repetition!".  Most likely all the other pages with binding in the project will look similar to this one so it'll be a lot of code to look through when something has to change.  

Solution 1:

In order to keep things more concise and manageable, what we should do is separate out the responsibility for binding to a simple generic helper class as follows.

public static class Binder
{
    public static void Bind<T>(ListControl target, IEnumerable<T> source, String dataTextField, String dataValueField)
    {
        target.DataSource = source;
        target.DataTextField = dataTextField;
        target.DataValueField = dataValueField;
        target.DataBind();
    }
} 

Since we have coded our helper class to the base ListControl class and the interface (IEnumerable<T>) we can use this same code for binding almost any type of collection with all the following objects.

  • BulletedList
  • CheckBoxList
  • DropDownList
  • ListBox
  • RadioButtonList

This will greatly simplify our calls and reduce our binding code throughout our project by about 75% (we went from four lines to one line).  Now our binding code looks like this:

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            IEnumerable<BizObject> source = BizObjectFactory.GetAll();

            Binder.Bind(myCheckBox, source, "Value", "Key");
            Binder.Bind(myDropDown, source, "Value", "Key");
            Binder.Bind(myBulletedList, source, "Value", "Key");
            Binder.Bind(myListBox, source, "Value", "Key");
            Binder.Bind(myRadioButtonList, source, "Value", "Key");
        }
    }
}

Which would you rather maintain?

Solution 2:

Better yet, if we always want to bind to the same key and value for a specific type of object, we can simplify this further by overloading the Binder.Bind() method as follows.

public static class Binder
{
    public static void Bind(ListControl target, IEnumerable<BizObject> source)
    {
        Bind(target, source, "Value", "Key");
    }

    public static void Bind<T>(ListControl target, IEnumerable<T> source, String dataTextField, String dataValueField)
    {
        target.DataSource = source;
        target.DataTextField = dataTextField;
        target.DataValueField = dataValueField;
        target.DataBind();
    }
}

Now we can have an even simpler syntax for our binding operations:

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            IEnumerable<BizObject> source = BizObjectFactory.GetAll();

            Binder.Bind(myCheckBox, source);
            Binder.Bind(myDropDown, source);
            Binder.Bind(myBulletedList, source);
            Binder.Bind(myListBox, source);
            Binder.Bind(myRadioButtonList, source);
        }
    }
}

Having this simple helper class will make life much easier (hint:  the same technique will work for windows projects).

Solution 3 (3.5 Framework):

If you are using the 3.5 Framework, we can simplify things even further by using extension methods:

public static class Binder
{
    public static void BindTo(this ListControl target, IEnumerable<BizObject> source)
    {
        Bind(target, source, "Value", "Key");
    }

    public static void BindTo<T>(this ListControl target, IEnumerable<T> source, String dataTextField, String dataValueField)
    {
        target.DataSource = source;
        target.DataTextField = dataTextField;
        target.DataValueField = dataValueField;
        target.DataBind();
    }
}

Now our consuming code will look like:

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            IEnumerable<BizObject> source = BizObjectFactory.GetAll();

            myCheckBox.BindTo(source);
            myDropDown.BindTo(source);
            myBulletedList.BindTo(source);
            myListBox.BindTo(source);
            myRadioButtonList.BindTo(source);
        }
    }
}

The added bonus with using extension methods is that intellisense will now pick up on whether the Bind() method is available for each of our UI elements.

I hope you find this code consolidation pattern helpful.

Until next time,
Happy coding


Similar Articles