WPF Binding, INotifyPropertyChanged and Linq

This is the first of a couple of tips I would like to share reguarding WPF. WPF binding is extremely powerful, but you are bound to run into a few issues, especially if, like myself, you have no WinForms experience. As I was writing my small LiveSpaceToBlogML GUI, I used binding in order to populate an object called ConversionOptions, which pretty much held all the data on the form.

The form look something like this (a pretty simplified version, in order to focus on what matters):

<Window x:Class="LiveSpaceToBlogML.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Window.Resources> <local:ConversionOptions x:Key="Options" BlogName="MyBlog" SecretWord="MySecret"/> </Window.Resources> <StackPanel DataContext="{Binding Source={StaticResource Options}}"> <TextBlock >Your Windows Live Spaces blog address:</TextBlock> <TextBox Text="{Binding Path=BlogName}"/> <TextBlock >Your Windows Live Spaces e-mail publishing secret word:</TextBlock> <TextBox Text="{Binding Path=SecretWord}"/> <TextBlock>Output file:</TextBlock> <TextBox Text="{Binding Path=OutputPath}"/> <Button Content="Convert!" x:Name="Convert" HorizontalAlignment="Center" Click="Convert_Click"/> </StackPanel> </Window>

As you can see I am binding the StackPanel and text-boxes inside of it to an object of type ConversionOptions. At first, that object looked something like this:

public class ConversionOptions { public string BlogName { get; set; } public string OutputPath { get; set; } public string SecretWord { get; set; } }

As you can see, pretty simple. Just three properties, and C# 3.0 new property syntax makes things pretty short for us. No need to actually declare the private fields and all. Anyway, this works nice enough and when the user clicks "Convert!" I have a ConversionOptions object filled with all the data from the text-boxes.

But something is missing. I want to be able to set default values on the text-boxes, by changing my Options object, and not the controls themselves. In fact, what I need is for the binding to work in the other direction. Changes to the ConversionOptions object should be reflected in the controls. Digging a little, I found the useful INotifyPropertyChanged interface, which apparently has been with us since the WinForms days. This interface has only one member - the PropertyChanged event, which you are supposed to call whenever a property changes. This, in turn, will notify WPF to update the controls on the screen.

Here's a simple implementation for my ConversionOptions:

public class ConversionOptions : INotifyPropertyChanged { private string _blogName; public string BlogName { get { return _blogName;} set { _blogName = value; OnPropertyChanged("BlogName"); } } private string _secretWord; public string SecretWord { get { return _secretWord; } set { _secretWord = value; OnPropertyChanged("SecretWord"); } } private string _outputPath; public string OutputPath { get { return _outputPath;} set { _outputPath = value; OnPropertyChanged("OutputPath"); } } protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; } }

As you can see, I am calling the PropertyChanged event whenever a property changes. There were two annoying things about writing this class:

  1.  I had to give up the cool property syntax. This was especially annoying as the new "prop" snippet only supports the short syntax, so I actually had to write the properties by myself (ReSharper doesn't support C# 3.0 yet, and that makes me sad).
  2. I had to name my properties in strings. That's never fun to do, as you don't get Refactoring (renaming a property will not change the string as well), intellisense and all that jazz. Strings suck.

This reminded me of a cool post I once read, by Jafar Husain. This extremely clever post talks about my problem exactly, and provides a great Linq solution. He uses the Linq Expression engine in order to enable access to property names in a strongly typed manner. This is the method Jafar wrote:

public static class SymbolExtensions { public static string GetPropertySymbol<T,R>(this T obj, Expression<Func<T,R>> expr) { return ((MemberExpression)expr.Body).Member.Name; } }

I won't explain it here, you can read about it in Jafar's post. The important thing is, this enabled me to change my class to this:

public class ConversionOptions : INotifyPropertyChanged { private string _outputPath; public string OutputPath { get { return _outputPath;} set { _outputPath = value; OnPropertyChanged(o => o.OutputPath); } } private string _blogName; public string BlogName { get { return _blogName;} set { _blogName = value; OnPropertyChanged(o => o.BlogName); } } private string _secretWord; public string SecretWord { get { return _secretWord; } set { _secretWord = value; OnPropertyChanged(o => o.SecretWord); } } protected virtual void OnPropertyChanged<R>(Expression<Func<ConversionOptions, R>> propertyExpr) { OnPropertyChanged(this.GetPropertySymbol(propertyExpr)); } protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; }

As you can see, the strings are no more! All I had to do is reference Jafar's GetPropertySymbol extension method, and add an overload to my own OnPropertyChanged method. The overload accepts an expression. Every property passes it a delegate that takes a ConversionOptions object and returns the property. In this manner, GetPropertySymbol can extract the property name for the method. And it's all type-safe, refactoring and intellisense friendly. Thank you Linq, and thank you Jafar.

Now everything works. We have two-way binding, and I can count on the fact that my window and my data are always consistent. A couple of notes for the end:

  1. I have no idea what is the performance for GetPropertySymbol. I have a feeling it might be rather reflection-like slow, but I need to study the issue more thoroughly to tell you for sure.
  2. The version of LiveSpaceToBlogML that I uploaded to this blog does uses the standard way of calling OnPropertyChanged. The reason being that I targeted the .NET 3.0 Framework when I built it, and using Linq requires .NET 3.5.

I will conclude by recommending again that you read Jafar's blog. He has some really pretty (or handsome?) Linq-related code there.

Published Friday, August 31, 2007 9:49 PM by dorony
תגים:, , , ,

Comments

# Expression trees' ConstantExpression values

After reading Jafar Husain 's posts ( 1 , 2 and bonus by Doron Yaacoby), I started thinking about being

Thursday, October 04, 2007 2:35 AM by Omer van Kloeten's .NET Zen

# re: WPF Binding, INotifyPropertyChanged and Linq

All this is fine, but the problem of not being able to use the new C# 3.0 syntax is still there. How do we solve that?

Thursday, January 17, 2008 5:28 AM by AtulGupta

# re: WPF Binding, INotifyPropertyChanged and Linq

I'm sorry, I don't understand your intent. What problem again?

Thursday, January 17, 2008 8:28 PM by dorony

# re: WPF Binding, INotifyPropertyChanged and Linq

Hi,

I've recently published an open source project which lets you do this, not only without using the string names, but also without using the Linq expressions.

It's called ActiveSharp, and details are here: www.codeplex.com/ActiveSharp

Properties written with ActiveSharp look like this:

public int Foo

{

 get { return _foo; }

 set { SetValue(ref _foo, value); }

}

ActiveSharp works with .NET 2 and later.

By the way, you're exactly right about the problem with having to give up the compact property syntax in this case.  I posted a feature request to Microsoft, with would solve that problem, here: connect.microsoft.com/.../ViewFeedback.aspx

Saturday, April 12, 2008 12:49 AM by John Rusk

# re: WPF Binding, INotifyPropertyChanged and Linq

You just need to create your own delegate that you can choose what parameters you wish to pass.

Check out, castalian.wordpress.com/.../progress-bar-delegate

using that example you could create an enum  (refactorable) and pass that(those) into the EventArgs instead. I do this frequently in my winforms applications.

Saturday, August 30, 2008 3:57 AM by Ira

# re: WPF Binding, INotifyPropertyChanged and Linq

I couldnt get your code to work without having the OnPropertyChanged implementation on every bindable type, rather than in a base type, but I made some changes that got it working. Usage is a bit more concise, too:

 class BindableObject : INotifyPropertyChanged

 {

   public event PropertyChangedEventHandler PropertyChanged;

   public static string GetPropertySymbol<TResult>(Expression<Func<TResult>> expr)

   {

     return ((MemberExpression)expr.Body).Member.Name;

   }

   protected virtual void OnPropertyChanged<PropertyType>(Expression<Func<PropertyType>> propertyExpr)

   {

     string propertyName = GetPropertySymbol(propertyExpr);

     if (PropertyChanged != null)

       PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

   }

 }

 class ProjectSettings : BindableObject

 {

   string _description;

   public string Description

   {

     get { return _description; }

     set

     {

       _description = value;

       OnPropertyChanged(() => Description);

     }

   }

   ...

Apologies if this is not readable. This blog software isnt that good for posting comments.

Wednesday, March 18, 2009 1:10 PM by commentor

# re: WPF Binding, INotifyPropertyChanged and Linq

Why not update the code in your prop snippet to create The extra typing code and not have to use reflection just to get the name of your type and it will also be more readable.

C:\Program Files\Microsoft Visual Studio 9.0\VC#\Snippets\1033\Visual C#

prop.snippet

Saturday, August 15, 2009 11:45 AM by Jaime

# re: WPF Binding, INotifyPropertyChanged and Linq

Because if the name of the property changes, I would have to change it in more than one place.

Sunday, August 16, 2009 6:17 PM by dorony

# re: WPF Binding, INotifyPropertyChanged and Linq

I could not get it to work. This is how I solved:

on the base class I created:

protected string GetPropertyName(int depth = 1)

{

  return (new StackTrace()).GetFrame(depth).GetMethod().Name.Split('_')[1];

}

and then modified the propierties

private string _secretWord;

public string SecretWord

{

  get { return _secretWord; }

  set

  {

   _secretWord = value;

   OnPropertyChanged(GetPropertyName());

   }

}

Friday, November 20, 2009 6:14 PM by Jose Angel

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: