In WPF, why doesn't TemplateBinding work where Binding does?

20,500

Solution 1

Found this forum post on MSDN: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0bb3858c-30d6-4c3d-93bd-35ad0bb36bb4/

It says this:

A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with

{Binding RelativeSource={RelativeSource TemplatedParent}}

Note from OP: Contrary to what it says in the documentation, in actuality, it should be this...

{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}

I filed a complaint against the docs, and while they did add a sentence now stating they are always one-way, the code example still doesn't list the mode, but I guess it's better than nothing.)

The TemplateBinding transfers data from the templated parent to the property that is template bound. If you need to transfer data in the opposite direction or both ways, create a Binding with RelativeSource of TemplatedParent with the Mode property set to OneWayToSource or TwoWay.

More in: http://msdn.microsoft.com/en-us/library/ms742882.aspx

Looks like Mode=OneWay is one of the "Optimizations" of using a TemplateBinding

Solution 2

TemplateBinding does not support two-way binding, only Binding does that. Even with your BindsTwoWayBeDefault option, it won't support two-way binding.

More info can be found here, but to summarize:

However, a TemplateBinding can only transfer data in one direction: from the templated parent to the element with the TemplateBinding. If you need to transfer data in the opposite direction or both ways, a Binding with RelativeSource of TemplatedParent is your only option. For example, interaction with a TextBox or Slider within a template will only change a property on the templated parent if you use a two-way Binding.

Share:
20,500
Mark A. Donohoe
Author by

Mark A. Donohoe

Large Developer at Large!

Updated on July 09, 2020

Comments

  • Mark A. Donohoe
    Mark A. Donohoe almost 4 years

    Ok... this is leaving me scratching my head. I have two WPF controls--one's a user control and the other's a custom control. Let's call them UserFoo and CustomFoo. In the control template for CustomFoo, I use an instance of UserFoo which is a named part so I can get to it after the template is applied. That works fine.

    Now both UserFoo and CustomFoo have a Text property defined on them (independently, i.e. not a shared DP using AddOwner. Don't ask...) that are both declared like this...

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text",
        typeof(string),
        typeof(UserFoo), // The other is CustomFoo
        new FrameworkPropertyMetadata(
            null,
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            null,
            null,
            true,
            UpdateSourceTrigger.PropertyChanged
        )
    );
    

    Notice specifically that the mode is set to TwoWay and the UpdateSourceTrigger is set to PropertyChanged, again for both.

    So in the style template for CustomFoo, I want to bind CustomFoo's Text property as the source to the internal UserFoo's Text property. Normally, this is easy. You just set UserFoo's text property to "{TemplateBinding Text}" but for some reason it's only going one way (i.e. UserFoo is properly set from CustomFoo, but not the reverse), even though again, both DPs are set for two-way! However, when using a relative source binding instead of a template binding, it works great! Um... wha??

    // This one works
    Text="{Binding Text, RelativeSource={RelativeSource AncestorType={local:CustomFoo}}, Mode=TwoWay}"
    
    // As does this too...
    Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
    
    // But not this one!
    Text="{TemplateBinding Text}"
    

    So what gives? What am I missing?