Sunday, May 27, 2012

WindowsForms Truss databinding


Yesterday I blogged about MVVM for WindowsForms and I mentioned Truss, the databinding library à la WPF that is UI independent. Note I said independent, not agnostic. In fact, it can bind to:
  1. an ordinary property contained in an object that implememts INotifyPropertyChanged
  2. a DependencyProperty of a DependencyObject
  3. an ordinary property of a WindowsForms control
When we say DependencyProperty you know it's WPF (or Silverlight).How come it knows how to bind to a property of a WinForms control?

Truss implements a binding strategy that uses the Microsoft PropertyNameChanged Pattern. Given a property Address, Truss attaches itself to the AddressChanged event. It happens properties of WinForms controls raise this event instead of  the PropertyChanged(<propertyname>) event that is raised by INotifyPropertyChanged properties.

A word about binding expressions

In our example, we have:
  • MainForm - the view with two text boxes: txName and txAddress 
  • MainFormViewModel - the ViewModel that contains a Model property of type MainFormModel
  • MainFormModel - the model with two string properties: Name and Address
The listing included in this post aren't MVVM examples, so I won't discuss the technical MVVM side. For this example, we pass the ViewModel reference to the view on the constructor method. On the form Load handler, we bind the model properties to the text boxes in two different ways:
  • using a simple string path expression
  • using a lambda expression
If you are going to inject your bindings using convention over configuration, you must use the first variation.

    Listing 1 - Single part path
    namespace WinFormsBinding
    {
        public partial class MainForm : Form
        {
            private MainFormViewModel _viewModel;
    
            public MainForm(MainFormViewModel viewModel)
            {
                _viewModel = viewModel;
                InitializeComponent();
            }
    
            private void MainForm_Load(object sender, EventArgs e)
            {
                var bindingManager = new BindingManager();
                bindingManager.Bindings.Add(new Binding(this.txAddress, "Text", _viewModel.Model, "Address"));
                bindingManager.Bindings.Add(new TypedBinding<TextBox, MainFormModel>(
                    this.txName, s => s.Text, _viewModel.Model, t => t.Name));
            }
        }
    }
    

    You might be tempted to refer the root object and use a multipart path.

    Listing 2 - Multi part path
    bindingManager.Bindings.Add(new Binding(this, "txAddress.Text", _viewModel, "Model.Address"));
    bindingManager.Bindings.Add(new TypedBinding<MainForm, MainFormViewModel>(
        this, s => s.txName.Text, _viewModel, t => t.Model.Name));

    Well don't. This doesn't work. I'm not sure whether this should work under Truss. I intend to come back on this subject.

    As a closing subject, on my quest for MVVM for WindowsForms I found another interesting piece of MVVM stuf. Magical.Trevor implements convention over configuration to find a View for a given ViewModel and bind both together. The same pattern is also used to self-bind:
    • Button Click event to methods
    • TextBox Text property to string properties
    Magical.Trevor isn't a port of Caliburn.Micro for WindowsForms. As Mike Minutillo says, it's based on some of the ideas found in Caliburn.Micro.

    2 comments:

    1. I tries Truss. When I change my object, TextBox updates OK, but when I change textBox.Text (by typing or by code) it does not update my object. I think this is because TextBox does not implement INotifyPropertyChanged. Am I forgetting something? Any workarounds?

      ReplyDelete
    2. Hi Mohammad,
      I'm not using Truss any more. The Codeplex project http://mvvmfx.codeplex.com/ includes the features of Truss. MVVM FX also includes some smal code samples.

      ReplyDelete