Injecting Xaml with Unity Application Block using Markup Extensions. Part 3.


Attached dependency properties. Bindings. Code behind support.

This is a continued research dedicated to Unity Application Block.

For more details please refer to the following posts

Part 1. Basic injection of custom user controls.

Part 2. Injection of standard WPF controls. Setting properties for injected controls.

Introduction

After having played by myself with injector markup extension implementation described in the previous article I found out a lot of limitations that could be easily introduced.

I used "PropertySetter" wrapper for delegating properties to the injected elements based on dependency property descriptors taken from string representation of property names. This restricted me either from setting common non-dependency properties or supporting Attached Properties. Though good for panel layouts injected controls for example couldn’t be properly positioned for the Canvas.

Another thing I wanted to have is ability for delegating dependency property bindings to injected control. This could be also a very interesting task as I found no ways of declaring Binding markup extension within the Xaml that are having deferred processing/execution.

Third feature I was looking for is correct referencing injected controls from the code behind. I wanted to be fully transparent delegating the names of UnityControl extensions to the injected controls so having possibilities of addressing them via .FindName(…) method

As the mission was successfully accomplished I’m putting the most interesting details here…

Prerequisites

As we are going to deal with Bindings we need getting one level upper in the type mapping as UIElement doesn’t support bindings. We will be using FrameworkElement for binding purposes. This requirement slightly impacts the Application Configuration File we are using for Container initialization. Instead of UIElement contract we have to declare FrameworkElement alias and map all the controls to it.

Here’s the sample:

<typeAlias alias="FrameworkElement" type="System.Windows.FrameworkElement, PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

The rest of the control aliases remain the same.

The container types are mapped to this alias accordingly like the following:

<types>
  <type type="FrameworkElement" mapTo="Button" name="UnityButton"/>
  <type type="FrameworkElement" mapTo="TabControl" name="UnityTabControl"/>
  <type type="FrameworkElement" mapTo="TextBox" name="UnityTextBox"/>
  <type type="FrameworkElement" mapTo="ListBox" name="UnityListBox"/>
</types>

(Attached) Dependency Properties Support

While it is more convenient maintaining dependency properties using DependencyPropertyDescriptor, common "plain" properties are maintained using Reflection – PropertyInfo. I’ve decided to decouple these two worlds to have capability of deciding which approach is more appropriate in this or that situation.

I’m using the standard Setter class for targeting (attached)dependency properties as this class may seem more intuitive than custom control implementation. Setter class also deals with DependencyProperty object and so provides native way of controlling the values defined. This is how our injection declaration looks like:

<this:UnityControl Name="btn1" Container="defaultProvider" Dependency="UnityButton">
    <this:UnityControl.Setters>
        <Setter Property="FrameworkElement.Height" Value="50"/>
        <Setter Property="Canvas.Left" Value="100"/>
        <Setter Property="Canvas.Top" Value="100"/>
    </this:UnityControl.Setters>
    
    <this:UnityControl.Properties>
        <this:PropertySetter Property="Content" 
                             Value="{StaticResource resourceString}"/>                    
    </this:UnityControl.Properties>
</this:UnityControl>

As you can see UnityControl extension now contains a collection property "Setters" accepting objects of "Setter" type. "Setters" collection is processed using dependency property descriptors only.

"Properties" collection property accepts objects of "PropertySetter" type and is processed using PropertyInfo. This means that PropertySetter can be also defined for a common dependency property, but it doesn’t support attached dependency properties. So as you can see this modification provides backwards compatibility for the samples from the previous article.

Main purpose of using standard Setter class as I’ve said earlier is restricting users from defining plain strings for property names. For the markup be valid each setter must have any valid dependency property defined. Thus PropertySetter accepts string based values and is more error prone. Each approach has it’s advantages and disadvantages so it’s up to you deciding which and at what time to use.

Dependency Property Binding Delegation

As you can understand the markup extensions’ inner world from the previous articles it’s quite difficult to control standard Binding markup extension. And guess it’s nearly impossible (at least for current version of WPF) subclassing it or providing deferred initialization/execution mechanisms. This means we cannot use default Binding extension for delegation purposes as we cannot control it’s life cycle.

The most easiest way was to follow the "PropertySetter" approach and introducing "BindingSetter" class for wrapping most basic functionality for establishing a binding.

Here’s the source code for such a wrapper:

public sealed class BindingSetter
{
  public DependencyProperty Property { get; set; }
  public string ElementName { get; set; }
  public DependencyProperty Path { get; set; }
  public BindingMode Mode { get; set; }
  public IValueConverter Converter { get; set; }
  public object ConverterParameter { get; set; }
  public object FallbackValue { get; set; }
}

So the main task for UnityControl extension will be enumerating the setters defined and constructing appropriate Bindings for the resolved FrameworkElement.

Let’s take a Canvas panel and inject the TextBox based control by "UnityTextBox" name like the following:

<this:UnityControl Name="txtBox" 
                   Container="defaultProvider" 
                   Dependency="UnityTextBox" 
                   Content="Default">
    <this:UnityControl.Setters>
        <Setter Property="FrameworkElement.Width" Value="100"/>
    </this:UnityControl.Setters>
</this:UnityControl>

Next let’s inject a Button based control named "UnityButton":

<this:UnityControl Name="btn1" Container="defaultProvider" Dependency="UnityButton">
    <this:UnityControl.Setters>
        <Setter Property="FrameworkElement.Height" Value="50"/>
        <Setter Property="Canvas.Left" Value="100"/>
        <Setter Property="Canvas.Top" Value="100"/>
    </this:UnityControl.Setters>
    
    <this:UnityControl.Bindings>
        <this:BindingSetter 
            Property="ContentControl.Content" 
            ElementName="txtBox" 
            Path="TextBox.Text" 
            Mode="OneWay"
            Converter="{StaticResource testConverter}"
            ConverterParameter="[{0}]"/>
    </this:UnityControl.Bindings>
</this:UnityControl>

Button will be have a Height value of 100 and will be positioned at (100,100) point at the Canvas. Also we define a one way binding between Button.Content and TextBox.Text. Also for demonstration purposes I’ve configured simple value converter and converter parameter. Converter is formatting the input string according to parameter template.

image

UnityControl markup extension takes the element resolved via Unity Container and configures the Binding manually. Of course it is not fully functional and there’s no MultiBinding support (guess this will require another enhancement for future articles) but demonstrates that it is possible to do.

 

Code Behind Support

It is obvious that you won’t get all the required functionality by declaring xaml only. It logically comes out that next thing you might require is accessing the injected elements from code behind. This is also required for Binding establishment. You might have already noticed the "Name" attribute definition in the samples above.

<this:UnityControl Name="txtBox" 
                   Container="defaultProvider" 
                   Dependency="UnityTextBox" 
                   Content="Default">
    <this:UnityControl.Setters>
        <Setter Property="FrameworkElement.Width" Value="100"/>
    </this:UnityControl.Setters>
</this:UnityControl>

Note that my implementation of Name property is not the same as x:Name markup extension. Name property is used in this case for delegating x:Name for the control being injected using the Unity Container. It is not mandatory so you can omit it if not required.

Upon injection UnityControl extension takes the Namescope of the parent container and registers the newly resolved element within it’s scope using the Name property if it was defined.

This brings out that the following Window.Loaded event handler be fully functional:

void Window1_Loaded(object sender, RoutedEventArgs e)
{      
  // Get the control injected with the UnityControl extension
  TextBox txtBox = canvas.FindName("txtBox") as TextBox;
}  

So this means that declaring injection in the Xaml you still have access to the element being injected and not taking care about the injector markup itself.

The source code for the article can be found here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s