Fluent Silverlight – Part 2 – Binding Properties

Introduction

After having introduced the new Fluent Silverlight framework in part 1 it is now time to dig a little bit deeper and discuss some implementation details. This time I’ll have a look into the details how we bind a property of a Silverlight control to a view model property.

As a reaction on our first post we have been asked why we prefer to define the binding in code rather than in XAML. We have different reasons for doing so. First of all we want to get rid of XAML as much as possible. Our application has a rather specific UI. The UI is hierarchical and highly dynamical. It’s not the type of UI one typically designs with tools like Microsoft Blend. Another reason is that XAML is not really wrist friendly and one defines the binding in XAML then one has to use a lot of “magic strings” and thus we have no compile time support regarding the correctness of the binding.

Binding Properties

We want to be able to bind specific properties of a Silverlight control to a property exposed by the view model. The binding shall be defined with a lambda expression and NOT with “magic strings”. We are using C# as our language which is a static typed language and thus want to leverage as much as possible the possibilities that a statically typed language offers. Typically we bind the Text property of a TextBox or a TextBlock to a corresponding property of the view model. This binding is defined in the view and is similar to this

this.WithTextBox(txtUsername)
    .Text(m => m.UserName)

To give a few other examples we can also e.g. bind the IsChecked property of a CheckBox or the Source property of an Image in a similar way to a respective view model property.

But not only business relevant data can or will be bound to controls like this but also other properties defining the visibility and interactability of a control can be bound. As an example take this code snippet

public override void Initialize()
{
    plainText = this.TextBoxFor(m => m.TextAnswer)
        .Interactable(m => m.TextAnswerInteractable)
        .Visible(m => m.TextAnswerVisible)
        .Watermark(m => m.TextAnswerWatermark)
        .VerticalAlignment.Top()
        .HorizontalAlignment.Stretch();
 
    Content = plainText;
}

In this sample besides the Text property we also bound the IsReadOnly and the Visibility properties of the TextBox to respective model properties. We even have something called Watermark which defines the text that is displayed in the TextBox if its content is empty and it does not currently have the focus.

The Visibility property of a Silverlight control is special in the sense that it is not of type boolean (that is visible = true or false) as one would naively expect but rather of type Visibility which is an enum defining the two values Visible and Collapsed. In a (view) model we rather prefer a property of type boolean to represent the fact whether a control displaying a certain business value is visible or not. Thus we need some kind of conversion happening when binding the control to the model. Fortunately the data binding used by Silverlight offers the possibility to defined so called value converters. The details of such a value converter are discussed in the next section.

Note: the visibility and interactability of a control displaying a business value is not a pure UI concern but rather determined by the given context. Part of the context are (among others) the current users rights.

Defining a custom value converter

To create a custom value converter we have to implement a class that implements the interface IValueConverter. This interface has two methods Convert(…) and ConvertBack(…). We want to convert a value of type boolean to a value of type Visibility and vice versa. So the implementation is very simple and is given below

public class BooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if(value == null) throw new ArgumentException("Cannot convert a null value to a visibility!");
 
        var sourceType = value.GetType();
        if (!(sourceType.Equals(typeof(bool)) && targetType.Equals(typeof(Visibility))))
            throw new ArgumentException(
                string.Format("Cannot convert type '{0}' to '{1}'. Only can convert boolean to Visibility.", 
                              sourceType.Name, targetType.Name));
 
        return (bool) value ? Visibility.Visible : Visibility.Collapsed;
        
    }
 
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if(value == null) throw new ArgumentException("Cannot back convert a null value to a boolean!");
 
        var sourceType = value.GetType();
        if (!(sourceType.Equals(typeof(Visibility)) && targetType.Equals(typeof(bool))))
            throw new ArgumentException(
                string.Format("Cannot convert type '{0}' to '{1}'. Only can convert Visibility to boolean.", 
                              sourceType.Name, targetType.Name));
 
        return (Visibility) value == Visibility.Visible;
    }
}

most of the above code is actually just there to prevent errors. The real meat is in the line

return (bool) value ? Visibility.Visible : Visibility.Collapsed;

that converts a boolean value to a corresponding Visibility; and in this line

return (Visibility) value == Visibility.Visible;

which converts a Visibility back to a boolean value.

The above class can now used to e.g. bind the property UserNameIsVisible (bool) of the view model to the Visibility property of a Silverlight control (element) as follows

var binding = new Binding("UserNameIsVisible") { Mode = BindingMode.OneTime };
binding.Converter = new BooleanToVisibilityConverter();
element.SetBinding(UIElement.Visibility, binding);                

The PropertyBinder helper class

Since we want to bind different properties of controls to the various properties of the view model we wanted to handle the intrinsic of the binding in one single place. Born was the PropertyBinder class. To define a binding we need 3 elements:

  • the name of the property of the view model to which we want to bind,
  • the property of the control which shall be bound and
  • the binding mode (OneTime, OneWay or TwoWay).

This leads us to the following method signature

public void SetBinding(Expression<Func<TModel, object>> expression, 
                       DependencyProperty property, 
                       BindingMode bindingMode)
{...}

The first parameter is a lambda expression and defines the name of the view model property to which we want to bind our control. From time to time we also need a custom value converter when binding a property. As a sample take the Visibility property discussed above. Thus we define an overload of the SetBinding method

public void SetBinding(Expression<Func<TModel, object>> expression, 
                       DependencyProperty property,
                       BindingMode bindingMode, 
                       IValueConverter valueConverter)
{...}

The former method will call the latter one and just pass null at the place of the value converter. This leads us to the following implementation

public class PropertyBinder<TModel> where TModel : class
{
    private readonly FrameworkElement element;
 
    public PropertyBinder(FrameworkElement element)
    {
        this.element = element;
    }
 
    public void SetBinding(Expression<Func<TModel, object>> expression, DependencyProperty property)
    {
        SetBinding(expression, property, BindingMode.OneWay);
    }
 
    public void SetBinding(Expression<Func<TModel, object>> expression, DependencyProperty property, BindingMode bindingMode)
    {
        SetBinding(expression, property, bindingMode, null);
    }
 
    public void SetBinding(Expression<Func<TModel, object>> expression, DependencyProperty property,
                           BindingMode bindingMode, IValueConverter valueConverter)
    {
        var accessor = ReflectionHelper.GetAccessor(expression);
 
        var binding = new Binding(accessor.Name) { Mode = bindingMode };
        if (valueConverter != null) binding.Converter = valueConverter;
 
        element.SetBinding(property, binding);
    }
}

Note that the name of the view model property which is described by the lambda expression is extracted from the expression with the aid of the ReflectionHelper utility class. This class we have “stolen” from the FuBu MVC project.

The above class will be heavily used by the rest of the framework.

The TextBoxBinder class – or – how to bind a property

Now that we have discussed the goals and paved the ground by defining the necessary utility or helper classes we finally want to define the logic needed to make this nice fluent interface possible that I showed above as well as in part 1 of this article series.

public class TextBoxBinder<TModel> : ControlBinder<TextBoxBinder<TModel>, TextBox, TModel> 
    where TModel : class
{
    public TextBoxBinder(TModel model, TextBox control)
        : base(model, control)
    {
    }
 
    public TextBoxBinder<TModel> Text(Expression<Func<TModel, object>> expression)
    {
        propertyBinder.SetBinding(expression, TextBox.TextProperty, BindingMode.TwoWay);
        return this;
    }
 
    // rest of code omitted for brevity...
}

In the constructor of the above class I have to pass the instance of the view model I want to bind to as well as the control which shall be bound. The class inherits from a ControlBinder which in turn inherits from a FrameworkElementBinder. The binder classes plus or minus copy the hierarchy of the controls. But I will discuss this in more detail in a subsequent post.

For the moment lets concentrate on the Text(…) method of the above class. The only parameter of the method is the lambda expression which defines to which model property I want to bind. I use the PropertyBinder class discussed above to define the binding. And then I just return this to make the fluent interface possible.

The view

The code behind of a XAML based user control looks similar to this

public partial class SampleView : IOpinionatedControl<SampleViewModel>
{
    public SampleView()
    {
        InitializeComponent();
    }
 
    public SampleViewModel Model
    {
        get { return DataContext as SampleViewModel; }
    }
 
    public void SetModel(IViewModel viewModel)
    {
        DataContext = viewModel;
    }
 
    public void Initialize()
    {...}
}

In the Initialize() method one would normally implement the code to bind the controls to the view model. That is code like this

this.WithTextBox(txtUsername)
    .Text(m => m.UserName)

Note that the user control (or let’s just call it view in the future) implements the generic interface IOpinionatedControl<TModel> where in this case the generic parameter TModel is equal to the view model SampleViewModel. This interface is defined as follows

public interface IOpinionatedControl<TModel> : IOpinionatedControl
    where TModel : class
{
    TModel Model { get; }
}

and it inherits from this interface

public interface IOpinionatedControl
{
    void SetModel(IViewModel viewModel);
    void Initialize();
}

which is its non generic variant.

Extension methods for the view

To permit us to easily create a TextBoxBinder instance for a given TextBox we now define extension methods for our views. Since all of our views implement the generic interface IOpinionatedControl<TModel> described above we will write the extension methods for objects which implement this interface.

public static class ControlBinderViewExtensions
{
    public static TextBoxBinder<TModel> WithTextBox<TModel>(this IOpinionatedControl<TModel> view, TextBox control)
        where TModel : class
    {
        return new TextBoxBinder<TModel>(view.Model, control);
    }
}

Note that the TextBoxBinder constructor expects the model as a first parameter. We have access to the view model via the view. We can then implement an additional extension method for each type of control we want to use.

Summary

In this post I have discussed the details how we bind a property of a Silverlight control to a property of the view model. The binding is declared via a lambda expression. A binding can optionally also contain a value converter to adapt UI specific types to more (view) model friendly types. As an example I have shown how we adapt the Visibility data type which is specific to Silverlight (and WPF) to the data type boolean which makes more sense in a model.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Gabriel Schenker

Gabriel N. Schenker started his career as a physicist. Following his passion and interest in stars and the universe he chose to write his Ph.D. thesis in astrophysics. Soon after this he dedicated all his time to his second passion, writing and architecting software. Gabriel has since been working for over 12 years as an independent consultant, trainer, and mentor mainly on the .NET platform. He is currently working as chief software architect in a mid-size US company based in Austin TX providing software and services to the pharmaceutical industry as well as to many well-known hospitals and universities throughout the US and in many other countries around the world. Gabriel is passionate about software development and tries to make the life of developers easier by providing guidelines and frameworks to reduce friction in the software development process. Gabriel is married and father of four children and during his spare time likes hiking in the mountains, cooking and reading.
This entry was posted in data binding, fluent Silverlight, MVVM pattern. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.bluespire.com/blogs Rob

    Instead of

    this.WithTextBox(txtUsername)
    .Text(m => m.UserName)

    Where you have to create explicit classes/methods for each control/property, why not use a more generic syntax like this:

    this.Bind(txtUsername).On(x => x.Text).To(m => m.UserName);

  • http://www.Chillisoft.co.zaHabanero Brett

    Hi I am a contributer to the Habanero Framework and have been doing several WPF Silverlight projects.
    Habanero does not implement an immediate solution to this (unlike its Winforms and Visual Web Gui solutions).

    They focus on Dynamic and programattically generating user interfaces this increases testability incredibly and eliminates error prone data binding.
    The fluent solution is the first match to this style of programming that I have found in the WPF/SL world. This makes sense to me