As a relative newcomer to Silverlight I was happily greeted by the warm feeling of familiarity when I started developing. It is surprisingly easy to make the transition from WPF to Silverlight developer, with most of the core concepts being just the same. However, there are some parts of the WPF framework that you start to miss. One of those is ElementName binding.
For those of you not familiar with the concept I will give a very brief overview. When binding the properties of your visual elements within XAML, the source of this binding will be the object associated with the elements DataContext. This works just fine for binding your business objects to the UI that exposes their properties. However, with WPF, binding gives you so much more than just a mechanism for exposing your business data, it also allows you to bind properties between visual elements. This is a powerful concept that allows you to create complex layouts, beyond that which is possible by the framework provided Panels (for some examples of this, see my article on creating a Bullet Graph). In order to enable this, WPF provides ElementName and RelativeSource bindings, giving you a powerful mechanism for locating other elements within your visual tree to bind to. A simple example, where a rectangle's Width is bound to a named slider is given below:
Unfortunately Silverlight does not have this capability.
My first thought was to simply point the DataContext of the target element to the source element in order to allow property binding between them. However, much to my surprise, Silverlight dependency properties do not support property changed notification.
A common solution to this problem is to employ a Relay Object, as described in a number of blog posts. An object with a single property, Value, which implement INotifyPropertyChanged is bound to the two visual elements. A simple example is illustrated below:
Here an instance of our RelayObject is bound to both the Rectangle's Width and the Slider's Value, effectively binding these to properties together. This works just fine, however it does not really feel like the WPF ElementName binding, furthermore, you have to add a new RelayObject instance for each binding.
My solution makes use of the Attached Behaviour pattern which is becoming very popular in WPF and Silverlight. First we define an attached property which uses a type which contains the information we need to create our binding, i.e source and target properties, and the name of the element which we wish to bind to.
What the above code does is it defines the attached property of type BindingProperties. The OnBinding method is invoked whenever this property is attached to a dependency object. Within this event handler, we add a handler to the element's Loaded event. This event occurs when the element is laid out within the visual tree and ready for action, it is at this point that we can perform our ElementName lookup.
Within the TargetElement_Loaded event handler we use the FrameworkElement.FindName to look-up the named source element for our binding. This method locates any element with the given name that is within the same XAML namescope as itself. Interestingly, this is the same method that Visual Studio uses for creating member variables within your code-behind file from the named elements within your XAML. Once, the named element has been located, a relay binding is constructed between them, as follows:
The above code simply creates our relay object, initialising its value from the sourtce element, then constructs the bindings from relay-to-source and relay-to-target. The only 'tricky' part of the above code is the use of reflection to locate the static dependency properties of each element.
Using the above attached property (behaviour), an element name binding can be constructed as follows:
The advantages of this approach are twofold, firstly, we do not have to explicitly create a relay object for each ElementName bindings, secondly, the source property value is used to initialise the relay object directly. It is also straightforward exercise to extend the above to add ValueConverters into the binding.
But what if we want to bind two different properties to our slider? If for example we want to bind the Widths of two rectangles, we would need to ensure that a single relay object is constructed which is shared by the Slider and both Rectangles. A simple solution to this problem is to maintain a dictionary of the source bindings to their associated relay objects. If we bind more than one target property to a particular source property, the relay object is re-used. Any value converters are specified on the target binding, therefore we can bind multiple properties to the source with different converters.
The following example shows a couple of Rectangles bound to our slider where their Widths are scaled by different factors:
And here is a demonstration of the above implemented both manually with relay objects and with this attached behaviour:
For details of the above, please refer to the attached project download.
This approch does have a few downsides - unfortunately the syntax is a little verbose. Also, in its present form you can only create on element name binding per visual element. However, the interesting feature of this technique is that it could be readily adapted to search for the source element in other ways, for example emulating the WPF relative source bindings. Perhaps I will have a go at that one next week ....
You can download the sourcecode for this blog post.