Wednesday 12 August 2009

Thread safe collection binding in WPF

The Microsoft Dot Net framework version 3.5 supports powerful binding between business objects and user interface elements. Before we get into the thorny cross-threading issues on collections as promised in the title, let's quickly lay some groundwork to establish a common understanding...
Let's say you have a business object like this:
   1:  public class MyObject
   2:  {
   3:      public string MyProperty { get; set; }
   4:  }
You now want to bind it to a TextEdit control on a WPF form. A trivial example would be to create a new WPF Form then dump the following inside the default control:-
Change the constructor in the code-behind to look like this:-
   1:  public Window1()
   2:  {
   3:      InitializeComponent();
   4:   
   5:      var obj = new MyObject
   6:      {
   7:          MyProperty = "Hello World"
   8:      };
   9:   
  10:      textBox1.SetBinding(
  11:          TextBox.TextProperty,
  12:          new Binding
  13:      {
  14:          Path = new PropertyPath("MyProperty"),
  15:          Source = obj,
  16:          Mode = BindingMode.TwoWay
  17:      });
  18:   
  19:  }
In this example we are creating an instance of MyObject, and at the same time initialising the MyProperty value on it. We then bind that property to the Text property on the TextEdit control. As you can see, I don't much care for writing bindings into the XAML. It may be less typing to do so, but I very seldom bind to static objects that exist at start-up. Most of my real-world programs tend to create bindings at run-time. Maybe I'll blog about that later. For now, just accept my word for it that this is the only "proper" way to bind in WPF.
The example we are building so far will work. But if you later change the MyProperty value of the object instance obj, the user-interface will not be updated. This is because the default setter of MyProperty does not tell anyone that the property is about to, or has changed. So let's fix that quickly...
   1:  public class MyObject : INotifyPropertyChanged
   2:  {
   3:      public MyObject() {    }
   4:   
   5:      string _myProperty;
   6:      public string MyProperty
   7:      {
   8:          get { return _myProperty; }
   9:          set
  10:          {
  11:              if (_myProperty != value)
  12:              {
  13:                  _myProperty = value;
  14:                  OnChange("MyProperty");
  15:              }
  16:          }
  17:      }
  18:   
  19:      public event PropertyChangedEventHandler PropertyChanged;
  20:      // helper method
  21:      protected void OnChange(string propName)
  22:      {
  23:          // is anyone listening?
  24:          if (PropertyChanged != null)    
  25:          {
  26:              var e = new PropertyChangedEventArgs(propName);
  27:              PropertyChanged(this, e);
  28:          }
  29:      }
  30:  }
These source objects usually need to implement either INotifyPropertyChanged or INotifyCollectionChanged, which is really simple and easy to do. Each time a public property (that can act as a binding source) is changed, you need to raise a PropertyChanged event, which means there's usually only a few changes to be made to a class to enable it to be bound.