Win-Forms data-binding is done via a String parameter which specifies the member name to be bound, e.g.
// SOURCE DATA class Person { int Age { get; set; } } // SOME GUI APPLICATION { // Create a TextBox and a Person. TextBox myTextBox = new TextBox(); Person myPerson = new Person; myPerson.Age = 10; // Bind the age of that person to the textbox's text property. myTextBox.DataBindings.Add("Text", myPerson, "Age"); }
If a member name is changed (either manually or F2 – e.g), more often than not, the data-binding that is bound to that member will not be updated, the program will compile successfully, and the problem will not be found until run-time (hopefully found before the product is shipped). e.g. If we rename the Age property of the Person to AgeInYears, this program will compile successfully, but will fail at run-time.
Instead, it would be better if we could force the compiler to help us find these errors.
// Instead of doing this. myTextBox.DataBindings.Add("Text", myPerson, "Age"); // I would like to do this instead. myTextBox.Bind(tb => tb.Text).To(myPerson, p => p.Age);
With the aid of the PropertyNameHelper.GetPropertyName, we could do this with the following code.
public static class BindingHelper { /// <summary> /// Returns a <c>BindingTarget</c> which can be used to bind a data source to a control. /// </summary> /// <typeparam name="T">The type of control to bind.</typeparam> /// <param name="controlToBind">The control to be bound.</param> /// <param name="expression">The expression representing the property of the control to bind to.</param> /// <returns>Returns the <c>BindingTarget</c> object which can be used to complete the binding process.</returns> public static BindingTarget Bind<T>(this T controlToBind, Expression<Func<T, object>> expression) where T : Control { // Simply return the binding target. return new BindingTarget(controlToBind, PropertyNameHelper.GetPropertyName<T>(expression)); } } /// <summary> /// Class that represents a control and property of the control, for data binding purposes. /// </summary> public class BindingTarget { /// <summary> /// Creates a new <c>BindingTarget</c> instance. /// </summary> /// <param name="controlToBind">The control to bind to the data source.</param> /// <param name="propertyName">The property of the control to bind, to bind the data source to.</param> internal BindingTarget(Control controlToBind, string propertyName) { // Store away the control to be bound and the property name. this.controlToBind = controlToBind; this.controlPropertyName = propertyName; } /// <summary> /// Binds the <c>ControlToBind</c> to the data member of the specified data source. /// </summary> /// <typeparam name="T">The type of the data source to be bound to.</typeparam> /// <param name="dataSource">The data source to be bound to.</param> /// <param name="dataMemberExpression">The expression representing the data member of the data source to bind to.</param> public void To<T>(T dataSource, Expression<Func<T, object>> dataMemberExpression) { // Extract the name of the data member, (field or property), of the data source, from the expression. string data_member_name = PropertyNameHelper.GetPropertyName(dataMemberExpression); // Bind the control to be bound, to the data member of the data source. this.controlToBind.DataBindings.Clear(); this.controlToBind.DataBindings.Add(this.controlPropertyName, dataSource, data_member_name); } /// <summary> /// The control to be bound to a data source. /// </summary> private Control controlToBind; /// <summary> /// The property name of the <c>ControlToBind</c> to bind the data source to. /// </summary> private string controlPropertyName; }