Silverlight: How to Bind Controls to your own Custom Model.

I've been reading a few comments and questions floating around in the Silverlight ethos around DataBinding and basically the how. The most common examples I've seen usually show the Binding capabilities within XAML but don't really break open the "code-behind" or more to the point, "how to bind to a custom model with code only".

Here's how (think ShoppingCart as your example):

The ShoppingCart (Model)

    1:      public class ShoppingCart : INotifyPropertyChanged, IModel
    2:      {
    3:   
    4:          string mycartname;
    5:   
    6:          public string MyCartName
    7:          {
    8:              get { return mycartname; }
    9:              set { mycartname = value; Notify("MyCartName"); }
   10:          }
   11:   
   12:          // boilerplate INotifyPropertyChanged code
   13:          void Notify(string mycartname)
   14:          {
   15:              if (PropertyChanged != null)
   16:                  PropertyChanged(this, new PropertyChangedEventArgs(mycartname));
   17:          }
   18:   
   19:          public event PropertyChangedEventHandler PropertyChanged;
   20:   
   21:      }

The ShoppingCart (View)

    1:      public partial class ShoppingCart : UserControl
    2:      {
    3:          ShoppingCart sh;
    4:          public ShoppingCart()
    5:          {
    6:              InitializeComponent();
    7:              sh = new ShoppingCart();
    8:              Binding bnd = new Binding("MyCartName");
    9:              bnd.Mode = BindingMode.OneWay;
   10:              bnd.Source = sh;
   11:              txtData.SetBinding(TextBox.TextProperty, bnd);
   12:              sh.MyCartName = "I was added after Binding";
   13:   
   14:              // Bind the RoutedEvent to handle a button click to update the
   15:              // Model attribute only.
   16:              btnUpdateCart.Click += new RoutedEventHandler(btnUpdateCart_Click);
   17:          }
   18:   
   19:          void btnUpdateCart_Click(object sender, RoutedEventArgs e)
   20:          {
   21:              sh.MyCartName = "I was added after a Click";
   22:          }
   23:   
   24:      }

Notes:

  • In the model you basically need to wire up the properties (setter/getters) to not only record the new value, but you also want to notify the rest of your application that the data within it has also updated (i.e. - If a tree in the forest falls and no one is around, did it make a sound? applies here).
  • bnd.Source is only used for the actual source, you can't "chain" properties onto it (eg: bnd.Source = sh.MyCartName will not work)
  • You can pass in the "Path" attribute via the new Binding("MyPath") construct. That or you can use the bnd.Path attribute as well. Either is good.
  • You need to obviously persist the Model, in this case I put it into the life of the View itself so that post it's initial binding, it can be used there after (provided it's parent doesn't die of natural causes).
  • Ignore the IModel implementation, as that's just part of my code (i.e. I needed to type cast the Models within my home-grown framework).
  • Make sure you implement INotifyPropertyChanged, as that's the missing key to making this work.
  • As you will see, I've got a method called "Notify() " which basically allows the setter/getters to do their work and delegate the responsibility of Notifying folks (other objects) that something has changed and react.

That's it, DataBinding 101 with Code Only - Look mah, no XAML.

I'm going to explore more about DependencyProperty later next week as I think we haven't covered them off as well as we could, no biggy, it's still beta 2 so all is forgiven.

Any suggestions, feedback around DataBinding please drop me a comment or email and will be more than happy to discuss them.