Silverlight – One ring to bring them all and in the darkness bind them

by DotNetNerd 12. January 2011 16:01

Lately I have had the chance to work with Silverlight, which suits me just fine since I am looking to get my hands on a HTC Mozart, and start building some apps. I was already in the process of reading Silverlight 4 in Action, so it all seems to come together at the moment.

Diving in I have been looking at bindings, which in turn made me look at the MVVM pattern and in the end Caliburn Micro. Anyway, I am getting ahead of myself, because what I want to write about today is how bindings work in Silverlight.

One ring to rule them all

As a guy who spends his day working with web applications I have done my fair share of mapping fields on objects to and from controls on webpages. I think I can safely say it is not one of the more challanging and inspiring parts of the job. So when frameworks come along that offer two-way bindings it kind of makes me feel a little warm and fuzzy inside. Luckily this is also doable in javascript – which is something I might get back to in a later blockpost.

Binding basics

There are three parts that come into play when adding bindings in Silverlight, which are the source, the target and the binding itself.


Binding

The source can be any regular CLR object, but if you need two way binding it must implement the INotifyPropertyChanged interface. A common way to do this is to have a base class that has a method like this:

public event PropertyChangedEventHandler PropertyChanged;

protected void NotifyPropertyChanged(string propertyName)
{
    if(PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

And then for each property call this method from the setter:

private string _name;
public string Name {
    get { return _name; }
    set
    {
        _name = value;
        NotifyPropertyChanged("Name");
    }
}

The binding is a type that describes how data should flow between source and target. It takes a path in the constructor that describes which property or indexer on the source to bind. Simply writing the name of a property will bind to that property, and dot notation is allowed to go deeper in the object graph. With indexes the quotes are left out so “[name]” would bind to the indexer that takes a string of “name”.

The mode property on the binding is used to set the binding direction to either  OneTime, OneWay or TwoWay. OneTime means that data is bound exactly ince from source to target. OneWay means that changes to the source will afterwards take effect on the taget. TwoWay means that data is bound from source to target and that any changes to either will take effect in both source and target.

So if you have a Person object and you wish to create a TwoWay binding to its Name property it can be done like this.

var binding = new Binding(“Name”) { Mode = BindingMode.TwoWay, Source = myPerson };

The target has to be a DependencyObject, some number of DependencyProperties. This is the case for all UIElements in the Silverlight framework. Most importantly this means that it has a SetValue method, which is used to set the binding.

To set the binding using you the set method you simply provide the depencendy property and the binding like so.

txtUser.SetBinding(TextBox.TextProperty, binding);

Another way is to set the binding declaratively. This way you can declare the binding and just set the datacontext in code.

XAML:

<TextBox x:Name="txtUser" Text="{Binding Name, Mode=TwoWay}"></TextBox>

Codebehind:

txtUser.DataContext = myPerson;

Lastly you can do it all declaratively using a resource this way.

<UserControl.Resources>   
    <local:Person x:Key="myPerson"/>
</UserControl.Resources>

<TextBox x:Name="txtUser" Text="{Binding Name, Mode=TwoWay, Source={StaticResource myPerson}}"></TextBox>

A thing to take note of is that if you set a dependency property directly any bindings to that field will be removed. So txtUser.Text = “John”; will remove the binding.

Binding collections

Not too many applications exist where you get away with just doing binding to TextBoxes, so luckily it is easy to bind to list controls as well.

When binding to an ItemsControl, which is a UIElements that binds to a collection of data, the path to the value that is displayed is set using the DisplayMemberPath property.

<ListBox x:Name="lstPeople" DisplayMemberPath="Name"
                 ItemsSource="{Binding Source={StaticResource PersonCollection}}"/>

Doing value convertion

Quite often when you bind data it is nessesary to change the type or representation of a value. This is done by implementing the IValueConverter interface which just has two methods – one for converting and one for converting back again. To give a simple example you could make a converter that converts decimal to string.

public class DecimalConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
              System.Globalization.CultureInfo culture)
    {
        return string.Format("{0:F2}", value ?? 0);
    }

    public object ConvertBack(object value, Type targetType,
              object parameter, System.Globalization.CultureInfo culture)
    {
        return decimal.Parse((value as string) ?? "0");
    }
}

The binding object has a Converter property, so all you need to do in order to have the binding use your IValueConverter is to set to an instance of DecimalConverter.

Strongly typed and less verbose ways of doing bindings

Setting up bindings is something that you get to do a lot when working with Silverlight. In order to make it a little less verbose and to avoid all the path magicstrings I implemented this simple extention method. This idea is to have a simple way to create a twoway binding for any INotifyPropertyChanged implementing object.

public static Binding CreateTwoWayBinding<T>(this T bindingSource, Expression<Func<T, object>> action, IValueConverter converter = null) where T : INotifyPropertyChanged
{
    var memberEx = action.Body as MemberExpression ?? ((UnaryExpression)action.Body).Operand as MemberExpression; 
    var binding = new Binding(memberEx.Member.Name)
              {
              Mode = BindingMode.TwoWay,
              Source = bindingSource,
              Converter = converter
              };
   
    return binding;
}

With this in place adding a binding becomes as simple as this.

txtBox.SetBinding(TextBox.TextProperty, item.CreateTwoWayBinding(i => i.PurchasePrice, new DecimalConverter()));

Tags:

Who am I?

My name is Christian Holm Diget, and I work as an independent consultant, in Denmark, where I write code, give advice on architecture and help with training. On the side I get to do a bit of speaking and help with miscellaneous community events.

Some of my primary focus areas are code quality, programming languages and using new technologies to provide value.

Microsoft Certified Professional Developer

Microsoft Most Valuable Professional

Month List

bedava tv izle