How to bind from a ContentTemplate to the surrounding custom Control?
Asked Answered
F

2

8

I've got the following user control:

<TabItem 
    x:Name="Self"
    x:Class="App.MyTabItem"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:App"
    >
    <TabItem.Header>
        <!-- This works -->
        <TextBlock Text="{Binding ElementName=Self, Path=ShortLabel, UpdateSourceTrigger=PropertyChanged}"/>
    </TabItem.Header>
    <TabItem.ContentTemplate>
        <DataTemplate>
            <!-- This binds to "Self" in the surrounding window's namespace -->
            <TextBlock Text="{Binding ElementName=Self, Path=ShortLabel, UpdateSourceTrigger=PropertyChanged}"/>

This custom TabItem defines a DependencyProperty 'ShortLabel' to implement an interface. I would like to bind to this and other properties from within the TabItem's DataTemplate. But due to strange interactions, the TextBlock within the DataTemplate gets bound to the parent container of the TabItem, which also is called "Self", but defined in another Xaml file.

Question

Why does the Binding work in the TabItem.Header, but not from within TabItem.ContentTemplate, and how should I proceed to get to the user control's properties from within the DataTemplate?

What I already tried

  • TemplateBinding: Tries to bind to the ContentPresenter within the guts of the TabItem.
  • FindAncestor, AncestorType={x:Type TabItem}: Doesn't find the TabItem parent. This doesn't work either, when I specify the MyTabItem type.
  • ElementName=Self: Tries to bind to a control with that name in the wrong scope (parent container, not TabItem). I think that gives a hint, why this isn't working: the DataTemplate is not created at the point where it is defined in XAML, but apparently by the parent container.

I assume I could replace the whole ControlTemplate to achieve the effect I'm looking for, but since I want to preserve the default look and feel of the TabItem without having to maintain the whole ControlTemplate, I'm very reluctant to do so.

Edit

Meanwhile I have found out that the problem is: TabControls can't have (any) ItemsTemplate (that includes the DisplayMemberPath) if the ItemsSource contains Visuals. There a thread on MSDN Forum explaining why.

Since this seems to be a fundamental issue with WPF's TabControl, I'm closing the question. Thanks for all your help!

Felid answered 29/9, 2008 at 13:46 Comment(4)
David Schmitt - it is maybe better that you post the information you have found as an answer and accept it? Cause there is no reason to close the question, and with the answer it can be helpful.Clausius
@MikroDel: I've no intention in grabbing rep for this answer. You're free to post and enhance what I've written under "Edit" in the question as answer. It doesn't change the fact that WPF doesn't support this specific use case.Felid
Its not about rep for answer. The answer can be also negativ - for example as here "WPF doesnt support...". I mean it will be easier for the other users to find the information if it will be placed as an answer and not in the question.Clausius
You can post it as answer and accept it. I dont wnat to do it (post as answer) cause I havent found this information.Clausius
S
2

What appears to be the problem is that you are using a ContentTemplate without actualy using the content property. The default DataContext for the ContentTemplate's DataTemplate is the Content property of TabItem. However, none of what I said actually explains why the binding doesn't work. Unfortunately I can't give you a definitive answer, but my best guess is that it is due to the fact that the TabControl reuses a ContentPresenter to display the content property for all tab items.

So, in your case I would change the code to look something like this:

<TabItem
    x:Class="App.MyTabItem"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:App"
    Header="{Binding ShortLabel, RelativeSource={RelativeSource Self}}"
    Content="{Binding ShortLabel, RelativeSource={RelativeSource Self}}" />

If ShortLabel is a more complex object and not just a string then you would want to indroduce a ContentTemplate:

<TabItem
    x:Class="App.MyTabItem"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:App"
    Header="{Binding ShortLabel, RelativeSource={RelativeSource Self}}"
    Content="{Binding ComplexShortLabel, RelativeSource={RelativeSource Self}}">
    <TabItem.ContentTemplate>
        <DataTemplate TargetType="{x:Type ComplexType}">
            <TextBlock Text="{Binding Property}" />
        </DataTemplate>
    </TabItem.ContentTemplate>
</TabItem>
Salgado answered 1/11, 2008 at 6:48 Comment(0)
I
1

Try this. I'm not sure if it will work or not, but

<TabItem 
    x:Name="Self"
    x:Class="App.MyTabItem"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:App"
    >
    <TabItem.ContentTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=ShortLabel}"/>
        </DataTemplate>
    </TabItem.ContentTemplate>
</TabItem>

If it doesn't work, try sticking this attribute in the <TabItem/>:

DataContext="{Binding RelativeSource={RelativeSource self}}"
Introversion answered 8/10, 2008 at 19:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.