Colin Eberhardt's Adventures in WPF

Silverlight ClipToBounds – Can I Clip It?, Yes You Can!

May 12th, 2009

With Silverlight, Panels do not clip their contents by default. See the following example:

noclip

Where we have a Grid containing another Grid which itself contains an ellipse, and a Canvas which contains an ellipse:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
 
    <Grid Grid.Column="0" Background="Blue" Margin="20">                        
        <Grid Background="Yellow" Margin="20,40,-20,20">
            <Ellipse Fill="LightGreen" Width="80" Height="80" Margin="-40, -40, 0, 0"/>
        </Grid>
    </Grid>
 
    <Canvas Grid.Column="1"  Background="Aqua" Margin="20" >
        <Ellipse Fill="Red" Canvas.Top="-10" Canvas.Left="-10" Width="130" Height="130"/>
    </Canvas>
</Grid>

Often this is not the desired effect (although it is actually quite a useful feature of Canvas; You can simply add a Canvas to your visual tree without explicitly or implicitly setting its Size and use it as a mechanism for absolute positioning its children).

Fortunately Silverlight provides a Clip property on UIElement, allowing you to provide the clipping geometry for the element:

<Grid Width="200" Height="100">       
    <Grid.Clip>
        <RectangleGeometry Rect="0, 0, 200, 100"/>
    </Grid.Clip>
</Grid>

The above example creates a clipping geometry which matches the rectangular geometry of the Grid itself. Clearly more funky clipping geometries can be created, allowing for really cool effects, however most of the time I simply want my Panel clipped so that its children cannot escape!

The above example has a few problems, Firstly, it is a bit long-winded having to explicitly create the geometry each time I want to clip; Secondly, if my Grid’s size is calculated from its parent’s layout, how can I define the clip geometry in my XAML?; Finally, if my Grid’s geometry changes, its clipped geometry does not change.

In order to solve this problem I created a simple little attached behaviour, which allows you to define the clipping using the attached property Clip.ToBounds as illustrated below:

<UserControl x:Class="SilverlightClipToBounds.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:util="clr-namespace:Util" Width="300" Height="200">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
 
        <Grid Grid.Column="0" Background="Blue" Margin="20" util:Clip.ToBounds="true">       
            <Grid Background="Yellow" Margin="20,40,-20,20" util:Clip.ToBounds="true">
                <Ellipse Fill="LightGreen" Width="80" Height="80" Margin="-40, -40, 0, 0"/>
            </Grid>
        </Grid>
 
        <Canvas Grid.Column="1"  Background="Aqua" Margin="20" util:Clip.ToBounds="true">
            <Ellipse Fill="Red" Canvas.Top="-10" Canvas.Left="-10" Width="130" Height="130"/>
        </Canvas>
    </Grid>
</UserControl>

The result can be seen below:

crop

And here is the code for the attached behaviour itself:

public class Clip
{
    public static bool GetToBounds(DependencyObject depObj)
    {
        return (bool)depObj.GetValue(ToBoundsProperty);
    }
 
    public static void SetToBounds(DependencyObject depObj, bool clipToBounds)
    {
        depObj.SetValue(ToBoundsProperty, clipToBounds);
    }
 
    /// <summary>
    /// Identifies the ToBounds Dependency Property.
    /// <summary>
    public static readonly DependencyProperty ToBoundsProperty =
        DependencyProperty.RegisterAttached("ToBounds", typeof(bool),
        typeof(Clip), new PropertyMetadata(false, OnToBoundsPropertyChanged));
 
 
    private static void OnToBoundsPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement fe = d as FrameworkElement;
        if (fe != null)
        {
            ClipToBounds(fe);
 
            // whenever the element which this property is attached to is loaded
            // or re-sizes, we need to update its clipping geometry
            fe.Loaded += new RoutedEventHandler(fe_Loaded);
            fe.SizeChanged += new SizeChangedEventHandler(fe_SizeChanged);
 
        }
    }
 
    /// <summary>
    /// Creates a rectangular clipping geometry which matches the geometry of the
    /// passed element
    /// </summary>
    private static void ClipToBounds(FrameworkElement fe)
    {
        if (GetToBounds(fe))
        {
            fe.Clip = new RectangleGeometry()
            {
                Rect = new Rect(0, 0, fe.ActualWidth, fe.ActualHeight)
            };
        }
        else
        {
            fe.Clip = null;
        }
    }
 
    static void fe_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        ClipToBounds(sender as FrameworkElement);
    }
 
    static void fe_Loaded(object sender, RoutedEventArgs e)
    {
        ClipToBounds(sender as FrameworkElement);
    }    
}

When the ToBounds property is associated with an element, ClipToBounds is invoked to create a rectangular clip geometry. We also add event handlers for Loaded, which is a very useful event which is fired when an element has been laid out and rendered, in other words it’s size will have been computed, and SizeChanged. In the event handlers for both we simply update the clipping geometry.

This can be seen in action here, where clicking on the Grids or Canvas increases their size, with the clipping geometry growing accordingly:

You can download a demo project here: sliverlightcliptobounds.zip

Can I Clip It?, Yes You Can!

Regards, Colin E.

Using CSS Selectors for Styling in WPF

March 11th, 2009

When I first encountered WPF I was really impressed by its styling and templating features which are more powerful than anything else I had previously seen for desktop software development. The property-value pairing within styles instantly reminded me of CSS, however the WPF styles lack the most powerful feature of CSS – the selector. This blog post describes an attached behaviour for styling WPF application using CSS selectors.

Let’s first look at the anatomy of CSS. A CSS stylesheet is composed of a number of rules, each rule is composed of a selector which provides a pattern which the CSS selector engine matches against the target HTML document to locate nodes which to apply the style to. The styling information itself is defined within a declaration block, which consists of a number of comma-separated declarations enclosed within braces. The declaration block looks quite similar to a style within WPF …

div {
   font-size: 10px;
}
<Style TargetType="TextBlock">
    <Setter Property="FontSize" Value="10"/>
</Style>

So let’s ignore the declaration block for the moment and concentrate on the interesting part, the CSS selector.

Selectors define a pattern which is matched, the information which the selector contains can include the element (tag) type, class or ID. More complex selectors might also include element attribute values or use pseudo-classes. We can easily find parallel concepts to these within WPF …

  • HTML element type (e.g. div, p, a) => dependency object type (e.g. TextBlock, Button)
  • HTML ID (#menu) => dependency object Name (e.g. x:Name=”menu”)
  • HTML class (.title) => attached class property (e.g. css:Css.Class=”menu”)

The only concept for which there really is no correspondent in WPF is CSS class. This can easily be introduced via an attached property.

So now that we have the concept mapping, all we need now is a CSS selector engine that can apply our selector in order to extract matching objects from our visual / logical tree. Luckily I found a .NET CSS selector engine implementation in codeplex called Fizzler, you can read about it on the authors blog, or go straight to codeplex to download it. The author of this engine, Colin Ramsay, had done a great job of the implementation and had also written an exhaustive set of unit tests. The engine itself was tightly coupled to HtmlDocumentNodes, however I was able to slide a simple interface between the selector engine and the HTML document types as follows:

public interface IDocumentNode
{
    IAttributeCollection Attributes { get; }
    List<IDocumentNode> ChildNodes { get; }
    IDocumentNode ParentNode { get; }
    IDocumentNode PreviousSibling { get; }
    string Id { get; }
    string Class { get; }
    string Name { get; }
    bool IsElement { get; }
}
 
public interface IAttributeCollection
{
    IAttribute this[string name] { get; }
}
 
public interface IAttribute
{
    string Value { get; }
}

Again, thanks to the extensive unit tests, this refactor only took ~ 40 minutes.

The next job was to create an implementation of the above interfaces that walks the visual / logical trees, both were pretty easy to implement using the LogicalTreeHelper and VisualTreeHelper classes. With these classes implemented, I was able to query the visual tree using selectors as follows:

SelectorEngine engine = new SelectorEngine();
IList<IDocumentNode> matchingNodes = engine.Parse(".form TextBlock.warning");
 
// apply the style to all matching nodes
foreach (DependencyObjectNode matchingNode in matchingNodes)
{
   ...
}

In the above example, the selector engine returns any TextBlock with the class ‘warning’, which is a descendant of any element with the class ‘form’. This is certainly a novel way of querying the visual tree!

The next step is to define the declaration block and apply the style which they define to any matching elements.

public class StyleDeclarationBlock : List<StyleDeclaration>
{
}
 
public class StyleDeclaration
{
    public string Property { get; set; }
    public object Value { get; set; }
}

We can construct style declarations within XAML as follows:

<css:StyleDeclarationBlock>
    <css:StyleDeclaration Property="BorderThickness" Value="2"/>
    <css:StyleDeclaration Property="BorderBrush" Value="Black"/>
    <css:StyleDeclaration Property="CornerRadius" Value="5"/>
    <css:StyleDeclaration Property="Margin" Value="10,10,10,10"/>
</css:StyleDeclarationBlock>

The process for applying a style is pretty straightforward, the only subtle complication is that styles, once applied are frozen (i.e. immutable). Therefore, each time an element is selected via our selector engine, we clone the existing style then merge it with the new one. See the attached source-code for details.

Putting it all together involves the creation of an Attached Behaviour which associates a stylesheet with an element within our XAML. When the element is loaded, the rules within the stylesheet are applied. Let’s look at a simple demo …

Here is a simple UI defined in XAML. Note the absence of any styling information:

<Grid>
    <Border css:Css.Class="form">                
        <StackPanel Orientation="Vertical">
            <TextBlock css:Css.Class="title" Text="User Details"/>
            <Grid VerticalAlignment="Top" >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>    
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>  
 
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
 
                </Grid.RowDefinitions>        
 
 
                <TextBlock css:Css.Class="mandatory" Text="Name"/>
                <TextBox  css:Css.Class="mandatory" Grid.Column="1" Text="Anthony Gatto"/>
 
                <TextBlock Grid.Row="1" Text="Age"/>
                <TextBox Grid.Row="1"  Grid.Column="1" Text="36"/>
 
                <TextBlock Grid.Row="2" Text="Profession"/>
                <TextBox Grid.Row="2"  Grid.Column="1" Text="Performer"/>
 
                <Button Grid.Row="3" Grid.Column="1" >Submit</Button>
            </Grid> 
        </StackPanel>
    </Border>
</Grid>

The resulting UI looks like this:

no-style

Now we apply our stylesheet to the root element in our XAML:

<css:StyleSheet x:Key="cssStyles">
    <css:StyleSheet.Rules>
 
        <css:StyleRule Selector=".form Grid *" SelectorType="LogicalTree">
            <css:StyleRule.DeclarationBlock>
                <css:StyleDeclarationBlock>
                    <css:StyleDeclaration Property="Margin" Value="4,4,4,4"/>
                </css:StyleDeclarationBlock>
            </css:StyleRule.DeclarationBlock>
        </css:StyleRule>
 
        <css:StyleRule Selector=".form TextBlock.mandatory">
            <css:StyleRule.DeclarationBlock>
                <css:StyleDeclarationBlock>
                    <css:StyleDeclaration Property="Foreground" Value="Red"/>
                </css:StyleDeclarationBlock>
            </css:StyleRule.DeclarationBlock>
        </css:StyleRule>
 
        <css:StyleRule Selector="Border.form">
            <css:StyleRule.DeclarationBlock>
                <css:StyleDeclarationBlock>
                    <css:StyleDeclaration Property="BorderThickness" Value="2"/>
                    <css:StyleDeclaration Property="BorderBrush" Value="Black"/>
                    <css:StyleDeclaration Property="CornerRadius" Value="5"/>
                    <css:StyleDeclaration Property="Margin" Value="10,10,10,10"/>
                </css:StyleDeclarationBlock>
            </css:StyleRule.DeclarationBlock>
        </css:StyleRule>
 
        <css:StyleRule Selector=".form .title">
            <css:StyleRule.DeclarationBlock>
                <css:StyleDeclarationBlock>
                    <css:StyleDeclaration Property="HorizontalAlignment" Value="Stretch"/>
                    <css:StyleDeclaration Property="HorizontalContentAlignment" Value="Center"/>
                    <css:StyleDeclaration Property="Background" Value="DarkBlue"/>
                    <css:StyleDeclaration Property="Foreground" Value="White"/>
                    <css:StyleDeclaration Property="FontSize" Value="13"/>
                    <css:StyleDeclaration Property="Padding" Value="3,3,3,3"/>
                    <css:StyleDeclaration Property="FontWeight" Value="Bold"/>
                </css:StyleDeclarationBlock>
            </css:StyleRule.DeclarationBlock>
        </css:StyleRule>
 
    </css:StyleSheet.Rules>
</css:StyleSheet>
 
...
 
<Grid css:Css.StyleSheet="{StaticResource cssStyles}">

Which styles our WPF application as follows:

styled

Let’s look at those selector in a little more detail. The first one, <css:StyleRule Selector=".form Grid *" SelectorType="LogicalTree"> is quite interesting. Here we are selecting everything within the Grid within our form and applying a margin. Note that here we are using a selector on the logical tree, otherwise we will also match elements within the control template of our TextBoxes and Buttons and will hence apply the margin internally within these elements also. However, in other contexts the ability to be able to apply styles within a controls template is a powerful concept.

The next selector <css:StyleRule Selector=".form TextBlock.mandatory"> styles our mandatory fields, the others styling the form title and borders.

The above is a pretty simple demonstration. The Fizzler CSS engine implements most of the CSS2.1 selectors and also some CSS3, so much more complex selector logic is possible. You can also include multiple comma-separated selectors on a rule.

Currently this code is not what I would call production-ready. It is just an idea that has been bugging me for a while that I wanted to get out of my head and share. I think this approach has a number of merits and it overcomes some of the restrictions in WPF styling. It provides a much more flexible mechanism for styling your UI, WPF gives you explicit styles and implicit styles, neither of which match the power of the CSS selector. This approach also adds the ability to merge styles, so if an element is matched by multiple rules these styles are merged. Furthermore, this approach does not impose the restriction that styles must have a TargetType, if the matching element does not have a corresponding dependency property, it simply does not pick up that piece of style information.

However, this implementation is not quite complete. I have not considered how to apply styles to dynamically generated content, ItemsControls for example.

I would be very interested to hear peoples opinions on this idea. Do you thin kit has potential? Perhaps if it was ported to Silverlight – it could even include mapping rules form ‘real’ CSS documents so that your Silverlight application could share style information with the rest of the web page which hosts it. But that is a job for another day …

You can download the code for this blog post here: wpfcssstyling.zip

UPDATE – the concept of using CSS to style WPF and Silvelight applications has been discussed on the WPF Discisples Group.

Regards, Colin E.

Implementing RelativeSource binding in Silverlight

February 27th, 2009

In my previous post I demonstrated how an the WPF ElementName style binding can be emulated with Silverlight via an attached behaviour. As a brief recap, the technique involved creating an attached property, which when bound, adds a handler for the elements Loaded event. When the element is loaded, the even handler locates the named element and constructs a binding via a relay object. Here is the attached property in use:

<Rectangle Height="20" Fill="Green">
    <local:BindingHelper.Binding>
        <local:BindingProperties ElementName="Slider" SourceProperty="Value" TargetProperty="Width"/>
    </local:BindingHelper.Binding>
</Rectangle>
<Slider x:Name="Slider" Value="20" Minimum="0" Maximum="300"/>

Where the Rectangle’s Width is bound to the Slider’s Value. For details, and sourcecode, visit this blog post. Here I am going to extend this attached behaviour in order to emulate WPF’s RelativeSource binding.

For much of the time you will want to bind your visual elements to your data objects via their DataContext property. However, there are often times when you want too perform a binding for pure presentation purposes, for example, binding the width of two elements together. In this context WPFs RelativeSource and ElementName binding prove to be powerful and useful features of the binding framework.

Here I will extend the attached behaviour I described in my previous post to add RelativeSource binding capabilities. The properties of the attached property type have been extended:

public class BindingProperties
{
    public string SourceProperty { get; set; }
    public string ElementName { get; set; }
    public string TargetProperty { get; set; }
    public IValueConverter Converter { get; set; }
    public object ConverterParameter { get; set; }
    public bool RelativeSourceSelf { get; set; }
    public string RelativeSourceAncestorType { get; set; }
    public int RelativeSourceAncestorLevel { get; set; }
 
    public BindingProperties()
    {
        RelativeSourceAncestorLevel = 1;
    }
}

With the RelativeSourceSelf, RelativeSourceAncestorType and RelativeSourceAncestorLevel properties being used for relative source bindings. Taking RelativeSourceSelf as our first example, within WPF a RelativeSource.Self property indicates that the source of a binding should be the element which the binding is associated with. (I know – it sounds a bit crazy, but search google, it is surprisingly useful!).

private static void OnBinding(
    DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
    FrameworkElement targetElement = depObj as FrameworkElement;
 
    targetElement.Loaded += new RoutedEventHandler(TargetElement_Loaded);
}
 
private static void TargetElement_Loaded(object sender, RoutedEventArgs e)
{
    FrameworkElement targetElement = sender as FrameworkElement;
 
    // get the value of our attached property
    BindingProperties bindingProperties = GetBinding(targetElement);
 
    if (bindingProperties.ElementName != null)
    {
        // perform our 'ElementName' lookup
        ...
    }
    else if (bindingProperties.RelativeSourceSelf)
    {
        // bind an element to itself.
        CreateRelayBinding(targetElement, targetElement, bindingProperties);
    }
}

When the attached property becomes attached to our target element it adds a handler for the elements Loaded event (this is the attached behaviour). Within the event handler, we determine whether this is a relative source binding. If this is the case, the CreateRelayBinding method is invoked where the source and target element parameters are the same element. For details of how the CreateRelayBinding method works, see the previous blog post. An example of a relative-source self binding is show below, where a TextBox’s Width property is bound to its Text, if you trype in a new text value, the TextBox Width adjusts accordingly.

<TextBox Text="200" Margin="0,5,0,0">
    <local:BindingHelper.Binding>
        <local:BindingProperties TargetProperty="Width" SourceProperty="Text"
                                 RelativeSourceSelf="True"/>                                
    </local:BindingHelper.Binding>
</TextBox>

The next type of RelativeSource binding I am going to tackle is the FindAncestor mode. You use this type of binding when you want to bind to an element of a specific type that is located further up the visual tree that the target element. The following code snippet shows how the attached behaviour achieves this type of binding:

private static void TargetElement_Loaded(object sender, RoutedEventArgs e)
{
    FrameworkElement targetElement = sender as FrameworkElement;
 
    // get the value of our attached property
    BindingProperties bindingProperties = GetBinding(targetElement);
 
    if (bindingProperties.ElementName != null)
    {
        // perform our 'ElementName' lookup
        ...
    }
    else if (bindingProperties.RelativeSourceSelf)
    {
        // bind an element to itself.
        ...
    }
    else if (!string.IsNullOrEmpty(bindingProperties.RelativeSourceAncestorType))
    {
 
        // navigate up the tree to find the type
        DependencyObject currentObject = targetElement;
 
        int currentLevel = 0;
        while (currentLevel < bindingProperties.RelativeSourceAncestorLevel)
        {
            do
            {
                currentObject = VisualTreeHelper.GetParent(currentObject);
            }
            while (currentObject.GetType().Name != bindingProperties.RelativeSourceAncestorType);
            currentLevel++;
        }
 
        FrameworkElement sourceElement = currentObject as FrameworkElement;
 
        // bind them
        CreateRelayBinding(targetElement, sourceElement, bindingProperties);
    }
}

The code above simply navigates up the visual tree to find the n’th element of a given type. When the given type has been located, the relay binding between the two is constructed.

This type of binding is incredibly flexible, the following shows a number of examples:

<UserControl x:Class="SilverlightBinding.PageTwo">
 
    <UserControl.Resources>
        <local:VisibilityConverter x:Key="VisibilityConverter"/>
    </UserControl.Resources>
 
    <StackPanel x:Name="TheOuterStackPanel" Background="White"> 
        <StackPanel Name="theInnerStackPanel" Width="400">
            <!-- bind the textbox width to its text property -->
            <TextBox Text="200" Margin="0,5,0,0">
                <local:BindingHelper.Binding>
                    <local:BindingProperties TargetProperty="Width" SourceProperty="Text"
                                             RelativeSourceSelf="True"/>                                
                </local:BindingHelper.Binding>
            </TextBox>
            <!-- bind the textbox text to this UserControls width property -->
            <TextBox Margin="0,5,0,0">
                <local:BindingHelper.Binding>
                    <local:BindingProperties TargetProperty="Text" SourceProperty="Width"
                                             RelativeSourceAncestorType="PageTwo"/>                                
                </local:BindingHelper.Binding>
            </TextBox>
            <!-- bind the textbox text to the inner stack panels name property -->
            <TextBox Margin="0,5,0,0">
                <local:BindingHelper.Binding>
                    <local:BindingProperties TargetProperty="Text" SourceProperty="Name"
                                             RelativeSourceAncestorType="StackPanel"/>                                
                </local:BindingHelper.Binding>
            </TextBox>
            <!-- bind the textbox text to the outer stack panels name property -->
            <TextBox Margin="0,5,0,0">
                <local:BindingHelper.Binding>
                    <local:BindingProperties TargetProperty="Text" SourceProperty="Name"
                                             RelativeSourceAncestorType="StackPanel"
                                             RelativeSourceAncestorLevel="2"/>                                
                </local:BindingHelper.Binding>
            </TextBox>
            <!-- bind a slider's value to its parent grid's height property  -->
            <Grid Height="30"  Margin="0,5,0,0" Background="CadetBlue" HorizontalAlignment="Left" >
                <Slider Width="400"  HorizontalAlignment="Left" Minimum="10" Maximum="40">
                    <local:BindingHelper.Binding>
                        <local:BindingProperties TargetProperty="Value" SourceProperty="Height"
                                             RelativeSourceAncestorType="Grid"/>
                    </local:BindingHelper.Binding>
                </Slider>
            </Grid>
        </StackPanel>     
    </StackPanel>
</UserControl>

You can see in the above XAML, examples of relative-source self and find ancestor bindings with a variety of types and ancestor-levels. And here it is in action:

If you type in a new value in the top text box its width will change, or move the slider to see the height of its parent grid changing. And all without any code-behind of course!

Many of these effects shown above could be performed via element-name bindings. However, you are not always the creator, or template provider for all the visual elements rendered to screen. One common example is that of the ItemsControl, the ListBox being an example of this type of control. Here you supply a DataTemplate, with your visual elements being rendered inside a ListBoxItem container. Therefore, there is no way to bind to the ListBoxItem via an element name or otherwise. However, you can reach the ListBoxItem by navigating up the tree using a relative-source binding as show below:

<ListBox Name="grid" Width="200" HorizontalAlignment="Left">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Width="180">
                <TextBlock Text="{Binding Forename}"/>
 
                <Ellipse Width="10" Height="10" Fill="Red" HorizontalAlignment="Right">
                    <local:BindingHelper.Binding>
                        <local:BindingProperties TargetProperty="Visibility" SourceProperty="IsSelected"
                                                 Converter="{StaticResource VisibilityConverter}"
                                                 RelativeSourceAncestorType="ListBoxItem"/>
                    </local:BindingHelper.Binding>
                </Ellipse>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

In the above example we have a DataTemplate which contains an ellipse. As the ListBox generates each ‘row’, it creates a ListBoxItem and populates it with the visual elements from our data template. When our ellipse is created, and loaded, the attached behaviour fires, navigating up the visual tree to find the first ListBoxItem it encounters. When it finds it, it creates a single instance of our relay object, binding both the ListBoxItem.IsSelected and Ellipse.Visibilty (via a suitable value converter) together via the relay object.

And here it is in action (click an item to see the ellipse) …

You can download a visual studio project with all the sourcecode: silverlightrelativesourcebinding.zip.

Enjoy, Colin E.

ElementName binding in Silverlight via Attached Behaviours

February 22nd, 2009

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:

<Rectangle Width="{Binding Path=Value, ElementName=MySlider}" Height="20" Fill="Green"/>
<Slider x:Name="MySlider" Value="25" Minimum="0" Maximum="300"/>

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:

<UserControl.Resources>
    <local:RelayObject x:Key="Relay">        
        <local:RelayObject.Value>
            <system:Double>20</system:Double>
        </local:RelayObject.Value>
    </local:RelayObject>
</UserControl.Resources>
 
<StackPanel x:Name="LayoutRoot" Background="White">
    <Rectangle Width="{Binding Path=Value, Source={StaticResource Relay}, Mode=TwoWay" Height="20" Fill="Green"/>
    <Slider Value="{Binding Path=Value, Source={StaticResource Relay}, Mode=TwoWay}" Minimum="0" Maximum="300"/>       
</StackPanel>

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.

public class BindingProperties
{
    public string SourceProperty { get; set; }
    public string ElementName { get; set; }
    public string TargetProperty { get; set; }
}
 
public static class BindingHelper
{
    public static BindingProperties GetBinding(DependencyObject obj)
    {
        return (BindingProperties)obj.GetValue(BindingProperty);
    }
 
    public static void SetBinding(DependencyObject obj, BindingProperties value)
    {
        obj.SetValue(BindingProperty, value);
    }
 
    public static readonly DependencyProperty BindingProperty =
        DependencyProperty.RegisterAttached("Binding", typeof(BindingProperties), typeof(BindingHelper),
        new PropertyMetadata(null, OnBinding));
 
 
    /// <summary>
    /// property change event handler for SelectAllButtonTemplate
    /// </summary>
    private static void OnBinding(
        DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement targetElement = depObj as FrameworkElement;
 
        targetElement.Loaded += new RoutedEventHandler(TargetElement_Loaded);
    }
 
    private static void TargetElement_Loaded(object sender, RoutedEventArgs e)
    {
        FrameworkElement targetElement = sender as FrameworkElement;
 
        // get the value of our attached property
        BindingProperties bindingProperties = GetBinding(targetElement);
 
        // perform our 'ElementName' lookup
        FrameworkElement sourceElement = targetElement.FindName(bindingProperties.ElementName) as FrameworkElement;
 
        // bind them
        CreateRelayBinding(targetElement, sourceElement, bindingProperties);
    }    
}

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:

private static readonly BindingFlags dpFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
 
private static void CreateRelayBinding(FrameworkElement targetElement, FrameworkElement sourceElement,
    string targetProperty, string sourceProperty, IValueConverter converter)
{
     // create a relay binding between the two elements
     ValueObject relayObject = new ValueObject();
 
     // find the source dependency property
     FieldInfo[] sourceFields = sourceElement.GetType().GetFields(dpFlags);
     FieldInfo sourceDependencyPropertyField = sourceFields.First(i => i.Name == sourceProperty + "Property");
     DependencyProperty sourceDependencyProperty = sourceDependencyPropertyField.GetValue(null) as DependencyProperty;
 
     // initialise the relay object with the source dependency property value 
     relayObject.Value = sourceElement.GetValue(sourceDependencyProperty);
 
     // create the binding for our target element to the relay object, this binding will
     // include the value converter
     Binding targetToRelay = new Binding();
     targetToRelay.Source = relayObject;
     targetToRelay.Path = new PropertyPath("Value");
     targetToRelay.Mode = BindingMode.TwoWay;
     targetToRelay.Converter = converter;
 
     // find the target dependency property
     FieldInfo[] targetFields = targetElement.GetType().GetFields(dpFlags);
     FieldInfo targetDependencyPropertyField = targetFields.First(i => i.Name == targetProperty + "Property");
     DependencyProperty targetDependencyProperty = targetDependencyPropertyField.GetValue(null) as DependencyProperty;
 
     // set the binding on our target element
     targetElement.SetBinding(targetDependencyProperty, targetToRelay);
 
     // create the binding for our source element to the relay object
     Binding sourceToRelay = new Binding();
     sourceToRelay.Source = relayObject;
     sourceToRelay.Path = new PropertyPath("Value");
     sourceToRelay.Mode = BindingMode.TwoWay;
 
     // set the binding on our source element
     sourceElement.SetBinding(sourceDependencyProperty, sourceToRelay);        
}

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:

<Rectangle Height="20" Fill="Green">
    <local:BindingHelper.Binding>
        <local:BindingProperties ElementName="Slider" SourceProperty="Value" TargetProperty="Width"/>
    </local:BindingHelper.Binding>
</Rectangle>
<Slider x:Name="Slider" Value="20" Minimum="0" Maximum="300"/>

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:

<Rectangle Height="20" Fill="Green">
    <local:BindingHelper.Binding>
        <local:BindingProperties ElementName="Slider" SourceProperty="Value"
            TargetProperty="Width" Converter="{StaticResource ScalingConverter}" ConverterParameter="2"/>
    </local:BindingHelper.Binding>
</Rectangle>
<Rectangle Height="20" Fill="Green">
    <local:BindingHelper.Binding>
        <local:BindingProperties ElementName="Slider" SourceProperty="Value"
            TargetProperty="Width" Converter="{StaticResource ScalingConverter}" ConverterParameter="4"/>
    </local:BindingHelper.Binding>
</Rectangle>
<Slider x:Name="Slider" Value="20" Minimum="0" Maximum="300"/>

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.