Introduction
This is the second post in my series about databinding in Silverlight and WPF. In the first post I looked at how you wire-up UI controls to a model in the absence of a databinding framework. I showed how databindings can be created in code-behind, removing the need for the various event handlers that the manual method requires, resulting in more readable code, where the connection between a UI control and a model property is all in one place. In this blog post I'll look at how bindings can be defined in XAML, providing a more concise and flexible method of wiring model objects to your view.
The rough outline for this series is as follows:
- Part One - Life before binding,
INotifyPropertyChanged
and creating bindings in code-behind - Part Two (this one) - The binding markup extensions, the
DataContext
and path syntax - Part Three - Other binding sources,
ElementName
,TemplatedParent
,TemplateBinding
- Part Four - Value converters
- Part Five - List binding
- ...
Code-behind binding: a re-cap
The previous blog post worked through the creation of a very simple application which creates a model object that represents an event (INotifyPropertyChanged
implementation omitted for clarity)
And render it with the following UI:
The XAML for the UI is as follows:
The previous blog post wired up the above UI by creating bindings in code-behind:
Most applications create bindings within the XAML markup ... and that is exactly what we shall do next!
Binding Markup Extension
It is quite unusual to see code which creates bindings in C# (code behind), this is because there is a simpler, more elegant way. Firstly, we'll change the code above to remove all of the bindings, and instead set the DataContext
of the page to the event that we wish to edit:
Next we'll update the XAML, removing the names of the controls used to edit this event (we only named them so that they were accessible form code-behind), and adding bindings as follows:
Compiling and running this modified version of our code yields exactly the same result. The UI displays the initial values of the event, and the bindings take care of updating the UI if the model changes and updating the model based on user interactions with the controls. So let's take a closer look at how these binding work.
Within XAML, any attribute value that is surrounded in curly braces {...}
is a markup extension. Whilst most of your XAML markup is simply used to construct controls and panels in order to assemble the visual tree of your application, markup extension inject extra functionality into the XAML parser. The Binding Markup Extension constructs a binding and associates it with the UIElement
or FrameworkElement
that the property belongs to.
As you might recall from the previous blog post, all bindings have a source object, source property, target object and target property:
How do these map to our XAML binding? Let's look at the various components of our markup:
The target object is the object that owns the property which we are binding to, i.e. the UI control rendering our data. The target property is the property that has been set via the markup extension, and the source property is the path of the binding.
However, there is something missing? Where is the source object? In our example above, where is the object that owns this Title
property we are binding to the UI? If you recall earlier we set the DataContext
property of our MainPage
to the event class which is being edited ... this is our source object. The DataContext
is a rather special property for a couple of reasons:
- Inheritence - The
DataContext
value is inherited down the visual tree from one control to the next. Even though we set theDataContext
ofMainPage
to our model object, if you set a breakpoint in the code and inspect theDataContext
of the aboveTextBox
you will also find that it is set to the same event object. - Default source - Any bindings that are defined without the source object being specified (via the
Binding.Source
property), will take theDataContext
of the target object as the source. In the case illustrated above this is theDataContext
of theTextBox
, which has been inherited fromMainPage
.
These two properties of DataContext
and the binding framework result in a concise and elegant way to wire up your applications user-interface.
The Property Path Syntax
We'll explore a few other features of the binding framework via a more complex example, this time we have a model object, PersonModel
, that has a relationship to another model object, AddressModel
. A summary of these classes is show below (INotifyPropertyChanged
implementation omitted for clarity):
Again, we create an instance of this class and set it as the DataContext
of our user control:
(Aren't C# object initializers just great!)
Binding the Surname
and Forename
to the UI is straightforward, but how about the WorkAddress
? The Path
property of a Binding
supports a special property path-syntax which has a dot notation for navigating relationships. We can bind our UI to the various properties of the Address as follows:
Which yields the following UI:
Now because Address implements INotifyPropertyChanged
, if we change the Street
or City
of the object bound to the UI, the binding framework will take care of pushing this change to the binding targets (i.e. the TextBox instances). But, what if we replace the Address
instance entirely? For example ...
Again, the binding framework updates the UI. The binding framework not only detects changes in the source property, it is able to detect changes at any point in the chain of property relationships from the DataContext
. That's pretty smart!
The property-path syntax supports binding to arrays and dictionaries via indexers, for example:
{Binding Path=ArrayOfThings[0]}
and
{Binding Path=DictionaryOfStuff["foo"]}
I'm not going to give examples of all the various bindings that are possible, MSDN has a good reference for these.
Creating Binding 'Islands'
In the previous example we had two bindings that navigated the relationship from Person to Address
to bind to properties of the Address
object. Instead of repeatedly navigating the same relationship, we can bind a 'region' of our UI to the WorkAddress
property.
If you recall, the DataContext
is inherited throughout the visual tree and is the default source for bindings. If we change the DataContext
of a common root element of our various Address bindings to the Person.WorkAddress
, we can simplify our bindings. We could set the DataContext
in code-behind, but there is a more elegant way:
In the above XAML we are binding the DataContext
of the Border
element to the WorkAddress
property of the Person instance (which was set as the DataContext
of our UI in code-behind). As a result, all the children of the Border
have the Address
instance as their DataContext
, so we can bind to the properties directly. This creates an 'island' within our UI that is bound to Address
as shown graphically below:
Binding Shorthand and Longhand
We'll wrap up part two of the series on databinding by looking at a couple of variations in the binding syntax, the first is a 'longhand' version of the binding. Instead of using the binding markup extension, it is possible to create the binding instance in XAML, for example, the simple binding to Forename
:
Can be expressed as follows:
This yields exactly the same result. What's the point in the longhand version? Good question, I have only used it occasionally myself, typically within multi-bindings, or where validation rules are being added.
If instead we want to make our bindings simpler, we can omit the Path and simply express the binding as follows:
Now there are some instances where you do not want to bind to a property of your source (i.e. DataContext
), rather you want to bind to the source itself. The syntax for this is a single 'dot':
Or, in this case, we can use the very-very short:
We'll see some instances of when this might be useful in a future part of this blog series on binding when we look at collection-binding.
Summary
In this blog post we have looked at the binding markup extension and how this provides a concise mechanism for creating bindings. We have also seen how the DataContext
, which is the default source for our bindings, plays a pivotal role in the Silverlight and WPF binding frameworks. Finally, we have seen how we can use bindings to 'switch' the DataContext
in order to create binding 'islands' within out UI.
In the next instalment we'll look at some of the other ways we can specify the binding source and when you might use them, but now we'll take a well-earned break ... see you next time.
You can download the sourcecode for the examples described in this blogpost: DatabindingExamplesPartTwo.zip
Regards, Colin E.