Udostępnij za pośrednictwem


WPF TemplateBinding vs. RelativeSource TemplatedParent

Trying to get the bindings right for a certain ControlTemplate I wanted to have for a TabControl lead me to an interesting observation on TemplateBinding that was not so obvious. In the TabControl's ControlTemplate, I wanted to have an inner ItemsControl binding to the templated TabControl's Items (and thus replace the default TabPanels).

It ended up working with a RelativeSource binding like this:

<ControlTemplate x:Key="CustomTabControlTemplate" TargetType="{x:Type TabControl}">

        ...

        <ItemsControl  Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" >

            <ItemsControl.ItemsSource>

                <Binding Mode="OneWay" Path="Items">

                    <Binding.RelativeSource>

                        <RelativeSource

                       Mode="TemplatedParent"

                       />

                    </Binding.RelativeSource>

                </Binding>

            </ItemsControl.ItemsSource>

        </ItemsControl>

I was intrigued that using the more usual TemplateBinding did not work (you get a compile time error):

<ItemsControl  ItemsSource="{TemplateBinding Items}" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" />

 From the TemplateBinding docs, one can get the (wrong) impression that it should actually work. It says:

A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent} Mode=OneWay}.  

The word "analogous" is misleading. At a search, it was clear that there are significant differences under the hood between Binding and TemplateBinding. Some of them are described here, however, none of them seemed to apply, which is why I tried to look again more carefully at the docs. And there's the explanation right on the TemplateBinding docs page: unlike a Binding, a TemplateBinding can only bind a DependencyProperty to another DependencyPropery (IMHO, the wording of "targetProperty" in the docs below is confusing/wrong from a WPF binding terminology perspective, because the property on the type being templated is actually the source, the target being propertyName !):

propertyName

 

DependencyProperty..::.Name of the property being set in the setter syntax.

 

targetProperty

A dependency property that exists on the type being templated, specified by its DependencyProperty..::.Name.

- or -

A "dotted-down" property name that is defined by a different type than the target type being templated. This is actually a PropertyPath. See PropertyPath XAML Syntax.

 

But Items in an ItemsControl is not a DependencyProperty (ItemsSource is !) which is why only a RelativeSource Binding works in this case.