How to make Databinding type safe and support refactoring?

C#.NetData BindingRefactoringType Safety

C# Problem Overview


When I wish to bind a control to a property of my object, I have to provide the name of the property as a string. This is not very good because:

  1. If the property is removed or renamed, then I don’t get a compiler warning.
  2. If a rename the property with a refactoring tool, then it is likely the data binding will not be updated.
  3. If the type of the property is wrong, e.g. binding an integer to a date chooser, then I don’t get an error until runtime.

Is there a design-pattern that gets round this, but still has the ease of use of data-binding?

(This is a problem in WinForms, ASP.NET, and WPF and possibly other systems.)

I have now found "workarounds for nameof() operator in C#: typesafe databinding" that also has a good starting point for a solution.

If you are willing to use a post processor after compiling your code, then NotifyPropertyWeaver is worth looking at.


Does anyone know of a good solution for WPF when the bindings are done in XML rather than C#?

C# Solutions


Solution 1 - C#

Note this answer uses WinForm and was written before C# had 'NameOf()'

Thanks to Oliver for getting me started I now have a solution that both supports refactoring and is type safe. It also let me implement INotifyPropertyChanged so it copes with properties being renamed.

It’s usage looks like:

checkBoxCanEdit.Bind(c => c.Checked, person, p => p.UserCanEdit);
textBoxName.BindEnabled(person, p => p.UserCanEdit);
checkBoxEmployed.BindEnabled(person, p => p.UserCanEdit);
trackBarAge.BindEnabled(person, p => p.UserCanEdit);

textBoxName.Bind(c => c.Text, person, d => d.Name);
checkBoxEmployed.Bind(c => c.Checked, person, d => d.Employed);
trackBarAge.Bind(c => c.Value, person, d => d.Age);

labelName.BindLabelText(person, p => p.Name);
labelEmployed.BindLabelText(person, p => p.Employed);
labelAge.BindLabelText(person, p => p.Age);

The person class shows how to implemented INotifyPropertyChanged in a type safe way (or see this answer for a other rather nice way of implementing INotifyPropertyChanged, ActiveSharp - Automatic INotifyPropertyChanged also looks good ):

public class Person : INotifyPropertyChanged
{
   private bool _employed;
   public bool Employed
   {
      get { return _employed; }
      set
      {
         _employed = value;
         OnPropertyChanged(() => c.Employed);
      }
   }
    
   // etc
    
   private void OnPropertyChanged(Expression<Func<object>> property)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, 
             new PropertyChangedEventArgs(BindingHelper.Name(property)));
      }
   }
    
   public event PropertyChangedEventHandler PropertyChanged;
}

The WinForms binding helper class has the meat in it that makes it all work:

namespace TypeSafeBinding
{
    public static class BindingHelper
    {
        private static string GetMemberName(Expression expression)
        {
            // The nameof operator was implemented in C# 6.0 with .NET 4.6
            // and VS2015 in July 2015. 
            // The following is still valid for C# < 6.0

            switch (expression.NodeType)
            {
                case ExpressionType.MemberAccess:
                    var memberExpression = (MemberExpression) expression;
                    var supername = GetMemberName(memberExpression.Expression);
                    if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;
                    return String.Concat(supername, '.', memberExpression.Member.Name);
                case ExpressionType.Call:
                    var callExpression = (MethodCallExpression) expression;
                    return callExpression.Method.Name;
                case ExpressionType.Convert:
                    var unaryExpression = (UnaryExpression) expression;
                    return GetMemberName(unaryExpression.Operand);
                case ExpressionType.Parameter:
                case ExpressionType.Constant: //Change
                    return String.Empty;
                default:
                    throw new ArgumentException("The expression is not a member access or method call expression");
            }
        }

        public static string Name<T, T2>(Expression<Func<T, T2>> expression)
        {
            return GetMemberName(expression.Body);
        }

        //NEW
        public static string Name<T>(Expression<Func<T>> expression)
        {
           return GetMemberName(expression.Body);
        }

        public static void Bind<TC, TD, TP>(this TC control, Expression<Func<TC, TP>> controlProperty, TD dataSource, Expression<Func<TD, TP>> dataMember) where TC : Control
        {
            control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));
        }

        public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)
        {
            // as this is way one any type of property is ok
            control.DataBindings.Add("Text", dataObject, Name(dataMember));
        }

        public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)
        {       
           control.Bind(c => c.Enabled, dataObject, dataMember);
        }
    }
}

This makes use of a lot of the new stuff in C# 3.5 and shows just what is possible. Now if only we had hygienic macros lisp programmer may stop calling us second class citizens)

Solution 2 - C#

The nameof operator was implemented in C# 6.0 with .NET 4.6 and VS2015 in July 2015. The following is still valid for C# < 6.0

To avoid strings that contain property names, I've written a simple class using expression trees to return the name of the member:

using System;
using System.Linq.Expressions;
using System.Reflection;

public static class Member
{
    private static string GetMemberName(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression) expression;
                var supername = GetMemberName(memberExpression.Expression);

                if (String.IsNullOrEmpty(supername))
                    return memberExpression.Member.Name;

                return String.Concat(supername, '.', memberExpression.Member.Name);

            case ExpressionType.Call:
                var callExpression = (MethodCallExpression) expression;
                return callExpression.Method.Name;

            case ExpressionType.Convert:
                var unaryExpression = (UnaryExpression) expression;
                return GetMemberName(unaryExpression.Operand);

            case ExpressionType.Parameter:
                return String.Empty;

            default:
                throw new ArgumentException("The expression is not a member access or method call expression");
        }
    }

    public static string Name<T>(Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

    public static string Name<T>(Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }
}

You can use this class as follows. Even though you can use it only in code (so not in XAML), it is quite helpful (at least for me), but your code is still not typesafe. You could extend the method Name with a second type argument which defines the return value of the function, which would constrain the type of the property.

var name = Member.Name<MyClass>(x => x.MyProperty); // name == "MyProperty"

Until now I haven't found anything which solves the databinding typesafety issue.

Solution 3 - C#

The Framework 4.5 provides us with the [CallerMemberNameAttribute][1], which makes passing the property name as a string unnecessary:

private string m_myProperty;
public string MyProperty
{
    get { return m_myProperty; }
    set
    {
        m_myProperty = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
    // ... do stuff here ...
}

If you're working on Framework 4.0 with KB2468871 installed, you can install the Microsoft BCL Compatibility Pack via nuget, which also provides this attribute. [1]: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx

Solution 4 - C#

This blog article raises some good questions about the performance of this approach. You could improve upon those shortcomings by converting the expression to a string as part of some kind of static initialization.

The actual mechanics might be a little unsightly, but it would still be type-safe, and approximately equal performance to the raw INotifyPropertyChanged.

Something kind of like this:

public class DummyViewModel : ViewModelBase
{
    private class DummyViewModelPropertyInfo
    {
        internal readonly string Dummy;

        internal DummyViewModelPropertyInfo(DummyViewModel model)
        {
            Dummy = BindingHelper.Name(() => model.Dummy);
        }
    }

    private static DummyViewModelPropertyInfo _propertyInfo;
    private DummyViewModelPropertyInfo PropertyInfo
    {
        get { return _propertyInfo ?? (_propertyInfo = new DummyViewModelPropertyInfo(this)); }
    }

    private string _dummyProperty;
    public string Dummy
    {
        get
        {
            return this._dummyProperty;
        }
        set
        {
            this._dummyProperty = value;
            OnPropertyChanged(PropertyInfo.Dummy);
        }
    }
}

Solution 5 - C#

One way to get feedback if your bindings are broken, is to create a DataTemplate and declare its DataType to be the type of the ViewModel that it binds to e.g. if you have a PersonView and a PersonViewModel you would do the following:

  1. Declare a DataTemplate with DataType = PersonViewModel and a key (e.g. PersonTemplate)

  2. Cut all PersonView xaml and paste it into the data template (which ideally can just be at the top of the PersonView.

3a. Create a ContentControl and set the ContentTemplate = PersonTemplate and bind its Content to the PersonViewModel.

3b. Another option is to not give a key to the DataTemplate and not set the ContentTemplate of the ContentControl. In this case WPF will figure out what DataTemplate to use, since it knows what type of object you are binding to. It will search up the tree and find your DataTemplate and since it matches the type of the binding, it will automatically apply it as the ContentTemplate.

You end up with essentially the same view as before, but since you mapped the DataTemplate to an underlying DataType, tools like Resharper can give you feedback (via Color identifiers - Resharper-Options-Settings-Color Identifiers) as to wether your bindings are broken or not.

You still won't get compiler warnings, but can visually check for broken bindings, which is better than having to check back and forth between your view and viewmodel.

Another advantage of this additional information you give, is, that it can also be used in renaming refactorings. As far as I remember Resharper is able to automatically rename bindings on typed DataTemplates when the underlying ViewModel's property name is changed and vice versa.

Solution 6 - C#

> 1.If the property is removed or renamed, I don’t get a compiler warning. > > 2.If a rename the property with a refactoring tool, it is likely the data binding will not be updated. > > 3.I don’t get an error until runtime if the type of the property is wrong, e.g. binding an integer to a date chooser.

Yes, Ian, that are exactly the problems with name-string driven data binding. You asked for a design-pattern. I designed the Type-Safe View Model (TVM) pattern that is a concretion of the View Model part of the Model-View-ViewModel (MVVM) pattern. It is based on a type-safe binding, similar to your own answer. I've just posted a solution for WPF:

http://www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM

Solution 7 - C#

x:bind (also called "compiled data bindings") for XAML (universal app) in windows 10 and windows phone 10 may solve this problem, see https://channel9.msdn.com/Events/Build/2015/3-635

I can't find the on line docs for it, but have not put much effort in, as it is something I will not be using for some time. However this answer should be a useful pointer to other people.

https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension

https://stackoverflow.com/questions/37398038/difference-between-binding-and-xbind

Solution 8 - C#

C# Markup seems to be solving the same set of problems, hence I am adding this answer as a pointer to help the current generation of programmers.

> Xamarin.Forms 4.6 introduced C# Markup, a set of fluent helpers and > classes that aim to make UI development in C# a joy. > > C# Markup helps developers write concise declarative UI markup and > cleanly separate it from UI logic, all in C#. Developers get to enjoy > C#’s first-class IDE support when writing markup. A single language > for markup and logic reduces friction, markup scattering and cognitive > load; there is less or no need for language bridging mechanisms like > separate converters, styles, resource dictionaries, behaviours, > triggers and markup extensions

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIan RingroseView Question on Stackoverflow
Solution 1 - C#Ian RingroseView Answer on Stackoverflow
Solution 2 - C#Oliver HanappiView Answer on Stackoverflow
Solution 3 - C#takrlView Answer on Stackoverflow
Solution 4 - C#nedruodView Answer on Stackoverflow
Solution 5 - C#Thorsten LorenzView Answer on Stackoverflow
Solution 6 - C#Harry von BorstelView Answer on Stackoverflow
Solution 7 - C#Ian RingroseView Answer on Stackoverflow
Solution 8 - C#Ian RingroseView Answer on Stackoverflow