53

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?

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • What happens if you use the long version of TemplateBinding where you can specify mode? I.E: Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay}" - This will tell us if it is an issue with the Mode or with TemplateBinding – Matt West May 06 '11 at 15:00
  • If you look above, I stated the long version works just fine. (I added a 2nd instance that uses TemplatedParent instead of explicitly setting the control type for clarity.) And again, the short version does SET the text of UserFoo as it should... it's just changes to UserFoo don't reflect back to CustomFoo. – Mark A. Donohoe May 06 '11 at 15:04
  • if you have specific questions take it to [meta] , and please read the [faq] – Jeff Atwood Jun 25 '11 at 11:46
  • 5
    @Jeff... what? This isn't a question for meta. It's for here. If you're the moderator that penalized me for flagging asking why I lost reputation for voting down bad info, that's because at the very top of the SO Meta FAQ, it says "Above all, be honest. If you see misinformation, vote it down. Add comments indicating what, specifically, is wrong. Provide better answers of your own. Best of all — edit and improve the existing questions and answers!" which is exactly what I did, so why am I penalized? – Mark A. Donohoe Jun 26 '11 at 11:51

2 Answers2

75

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

Timo Salomäki
  • 7,099
  • 3
  • 25
  • 40
Matt West
  • 2,874
  • 1
  • 19
  • 13
  • Actually, if you look at the MSDN link, it doesn't say anything about being OneWay, just `"{Binding RelativeSource={RelativeSource TemplatedParent}}"` I'm not saying that's the wrong answer... I'm saying I don't see where MS says it's OneWay by default, but I do believe it since that's what I'm seeing too. Still, MS should've (more clearly if they did) document that instead of letting us believe it's just a nice shortcut. – Mark A. Donohoe May 06 '11 at 15:12
  • 2
    I agree - its frustrating that you have to find it on some response to a question on their Forum. They should update the MSDN page to reflect that. – Matt West May 06 '11 at 15:30
  • I flagged the page as being wrong, explaining how that simple omission costed me days of research when having it would have saved it all. – Mark A. Donohoe Jun 16 '11 at 12:55
  • I see they finally updated the docs with a comment on it being one-way, but they still have the code statement itself wrong as it doesn't show the mode. I've flagged their article yet again stating it really needs to be added as the comment isn't enough. What they have is *not* analogous as they claim. – Mark A. Donohoe Mar 14 '14 at 14:37
  • Looks like MS finally updated the docs and code sample to include `Mode=OneWay` so hopefully this will no longer be a confusing issue. (I came back because this was just upvoted this week.) – Mark A. Donohoe Jul 18 '22 at 16:26
11

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.

CodeNaked
  • 40,753
  • 6
  • 122
  • 148
  • 1
    Voted you up because of the other info found there, but this really should've been in the MSDN documentation and not just some blog regardless of who wrote it. People expect the docs to be the end-all, be-all and MSDN missed that 'oneway' bit which is kind of important. – Mark A. Donohoe May 06 '11 at 16:14
  • @MarqueIV - Yeah, I agree. But there are lots of things you won't find in the docs, or are so briefly described that no search engine would ever find it ;-) – CodeNaked May 06 '11 at 16:29