Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make WPF data bindings refactor safe?

So I am working through my first WPF project and I am liking what I see so far. There was more of learning curve than what I anticipated, but nevertheless WPF is pretty cool. However, I am struggling a little bit with the data binding concepts. One specific question I have is how do I make my data binding declarations refactor safe? Consider this example.

public class MyDataObject
{
  public string FooProperty { get; set; }
}

void Bind() 
{
  var gridView = myListView.View as GridView;
  gridView.Columns.Clear();
  gridView.Columns.Add(
    new GridViewColumn() 
      { 
        Header = "FooHeader", 
        DisplayMember = new Binding("FooProperty")
      }
    );
  List<MyDataObject> source = GetData();
  myListView.ItemsSource = source;
}

So what if I rename the FooProperty on my data object to something else? The data binding will be invalid and I will not get a compile error since the binding was declared via text only. Is there a way to make the binding a little more refactor safe?

like image 745
Brian Gideon Avatar asked Sep 11 '09 20:09

Brian Gideon


2 Answers

You could use a lambda expression to express the property name, rather than using the name directly :

    protected static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
    {
        if (expression.NodeType == ExpressionType.Lambda && expression.Body.NodeType == ExpressionType.MemberAccess)
        {
            PropertyInfo prop = (expression.Body as MemberExpression).Member as PropertyInfo;
            if (prop != null)
            {
                return prop.Name;
            }
        }
        throw new ArgumentException("expression", "Not a property expression");
    }

You would use it like that :

...
DisplayMember = new Binding(GetPropertyName((MyDataObject o) => o.FooProperty))
...

OK, it's a bit verbose... If you want something shorter, you could also create a helper method :

public Binding CreateBinding<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
{
    return new Binding(GetPropertyName(expression))
}

...
DisplayMember = CreateBinding((MyDataObject o) => o.FooProperty)
...

That way, the refactoring should work fine if you rename the property (except in the XAML of course...)

like image 193
Thomas Levesque Avatar answered Nov 13 '22 02:11

Thomas Levesque


Refactoring relies upon tool support recognising when a particular symbol in code (C#, XAML, config etc...) represents the identifier being renamed.

In the example you give, the string literal "FooProperty" could not be 100% construed as belonging to MyDataObject without special knowledge of the inner workings of GridView and by extension all other types in WPF and other frameworks.

However, in a DataTemplate, it's possible to be 99% sure:

<DataTemplate DataType="{x:Type local:MyDataObject}">
    <TextBlock Text="{Binding Path=FooProperty}" />
</DataTemplate>

I use (and swear by) an IDE plugin called ReSharper (aka R#) which is a very intelligent about these kinds of things. If you rename FooProperty, R# will rename the property for you automatically.

In your example, if you were to rename the property, R# would still be of use. It finds all instances of the string in literals (your case) and comments (very useful if you've commented out some code and might uncomment it later). You're provided with a tree view that shows each literal in context, and you can check/uncheck individual usages/files/folders/projects before proceeding.

If your budget allows, get R#. If your budget doesn't allow, download a trial and by the end of it, your budget will find room. Make sure you print out a copy of the shortcut keys to improve your learning experience.

like image 33
Drew Noakes Avatar answered Nov 13 '22 02:11

Drew Noakes