WPF/Silverlight Custom Control Binding Gotcha

July 8, 2011

3 comments

When creating custom controls (not user controls) in WPF or Silverlight, the control creator typically supplies a default control template to give a default look to her control, but a client can change that control template using the Template property (inherited from Control). This is the standard way of working.

If some part of the template requires data binding, it’s not supplied with the default template: if it did, a client wanting to replace the template would have to correctly preserve the data binding expressions, which may be difficult or even impossible (if converters are involved, for instance). Instead, the control author creates the bindings in code, alleviating this burden off the client.

Here’s a simple custom control template example:

<Style TargetType="{x:Type local:DemoControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:DemoControl}">
                <Border Background="{TemplateBinding Background}"
                       BorderBrush="{TemplateBinding BorderBrush}"
                       BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Slider x:Name="PART_Slider" Minimum="0" Maximum="100" />
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter
>
</
Style
>

The control itself exposes a dependency property that should be data bound to the supplied slider (if any). The way to “find” that slider is to name it explicitly and advertise on the control class that this named part is significant, and should be supplied:

[TemplatePart(Name = DemoControl.SliderPartName, Type = typeof(RangeBase))]
public class DemoControl : Control {
    public const string SliderPartName = "PART_Slider";

The TemplatePart attribute indicates which name is needed and what minimum type of element is acceptable.

Now, when the control’s template is applied (whether the default template or a custom one), the control looks up the element in its OnApplyTemplate override and hooks data binding, events, or whatever else may be necessary for the control’s intended behavior.

Suppose that in our control, the following dependency property is defined and we would like it to data bind to the found “slider” (something that derives from RangeBase), and that that binding should be a two way binding. Here’s the property’s definition (WPF):

public double TheValue {
    get { return (double)GetValue(TheValueProperty); }
    set { SetValue(TheValueProperty, value); }
}

public static readonly DependencyProperty TheValueProperty =
    DependencyProperty.Register("TheValue", typeof(double), typeof(DemoControl), new UIPropertyMetadata(0.0));

Here’s the start of the OnApplyTemplate override:

public override void OnApplyTemplate() {
    base.OnApplyTemplate();

    var slider = GetTemplateChild(SliderPartName) as RangeBase;
    if(slider != null) {

Now we need to connect the dependency property to the Value property of the RangeBase object.

Here’s one way to make the connection, assuming the requested control exists:

var binding = new Binding("Value");
binding.Mode = BindingMode.TwoWay;
binding.Source = slider;
BindingOperations.SetBinding(this, TheValueProperty, binding);

Since it’s a two way binding (and no converter is needed), there is another way:

var binding = new Binding("TheValue");
binding.Mode = BindingMode.TwoWay;
binding.Source = this;
BindingOperations.SetBinding(slider, Slider.ValueProperty, binding);

This apparently achieves the same thing: both are dependency properties, no converter is needed, so why should we care who is the source and who is the target?

Let’s place the control in some WPF window:

<Window x:Class="CustomControlDemo.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="clr-namespace:CustomControlDemo"
       Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <local:DemoControl x:Name="_demo" Margin="10" TheValue="{Binding Text, ElementName=_text1, Mode=TwoWay}"/>
        <TextBox x:Name="_text1" Grid.Row="1"/>
    </Grid
>
</
Window
>

We’ve bound the TheValue property to a text box, so that moving the slider (from the default template) changes the text and vice versa. Surprisingly enough, this works with one of the previous data binding options, but not both. Can you guess which is the “good one”?

The answer is that the first option fails, while the second succeeds. Why? The binding seems the same – it’s two way, no converter, what’s going on?

Curiously enough, if we change the binding in the client, so that the textbox Text property is bound to the TheValue property – everything works in both binding ways!

The solution to this conundrum is that in WPF/Silverlight, a target dependency property may be bound by a single data binding expression. That means that in the following line:

BindingOperations.SetBinding(this, TheValueProperty, binding);

The target is the TheValue property, meaning that it’s bound to something else (as target), and such it loses its original binding.

The moral of the story is: when you’re writing custom controls, bind your dependency properties as source and not as target whenever possible, to give your clients more freedom on how to data bind properties in your control.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

3 comments

  1. JoryJuly 15, 2011 ב 01:30

    These tiopcs are so confusing but this helped me get the job done.

    Reply
  2. AnonymousJuly 2, 2012 ב 22:24

    Thank you SIR! Great Catch

    Reply
  3. Craig SOctober 6, 2013 ב 18:14

    Great article. I read Matthew MacDonald’s Pro WPF in C# 2010 and was initially confused by PART_xxx vs. SomeTemplateElement names in regards to the TemplatePartAttribute and then using the VisualStateManager. This cleared it up for me; and when I went back and re-read Matthew MacDonald’s section on this, it made much more sense.

    Reply