Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a nested WPF View where I pass Objects through to the inner layers via Dependency Properties.
My outer XAML layer passes data into RetentionChartControl.ChartParams:
<local:RetentionChartControl ChartParams="{Binding PeakRow.Chartparams[0], RelativeSource={RelativeSource AncestorType={x:Type local:PeakrowChartGroupControl}}}"></local:RetentionChartControl>
In my most inner layer I have the following XAML code:
<UserControl x:Class="DryLab.Peakmovement.RetentionChartControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DryLab.Peakmovement"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
x:Name="uc"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<lvc:CartesianChart x:Name="cc" Series="{Binding ChartParams.Seriescollection, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}}}" >
<lvc:CartesianChart.DataTooltip>
<lvc:DefaultTooltip SelectionMode="OnlySender" />
</lvc:CartesianChart.DataTooltip>
<lvc:CartesianChart.AxisY>
<lvc:Axis Foreground="Black" Title="{Binding ChartParams.TitleAxY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:RetentionChartControl}} }" LabelFormatter="{Binding ChartParams.Formatter, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}}}">
</lvc:Axis>
</lvc:CartesianChart.AxisY>
<lvc:CartesianChart.AxisX>
<lvc:Axis Foreground="Black" Title="{Binding ChartParams.TitleAxX, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}}}" Labels="{Binding ChartParams.Labels, RelativeSource={RelativeSource AncestorType={x:Type local:RetentionChartControl}} }"></lvc:Axis>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
</Grid>
</UserControl>
Now the Binding for <lvc:Cartesian Chart> (From LiveCharts https://lvcharts.net/App/examples/v1/wpf/Basic%20Line%20Chart) is working, the ChartParams Variable is fully evaluated and i can Access the Seriescollection.
However if i look at the deeper nested <lvc:Axis> (via LiveVisualTree) the Title Property is never evaluated.
What have i missed? Why cant i bind to ChartParams from within <lvc:Axis>?
Any help is appreciated, will post further code if necessary.
Edit:
I should add, that when the application is running, and I touch my Axis.Title in Xaml, it reevaluates and then shows the results as espected
The problem is, that the Axis element is not added to the visual tree the moment the bindings are resolved. All visible elements that are a child of the chart's underlying Canvas, are added later (long time after the chart control's FrameworkElement.Loaded event was raised). Therefore data bindings that bind e.g. Axis or Separator etc. to their visual parent using RelativeSource will point to nowhere (unless you set the binding after the relevant target elements are added to the Canvas (requires C# instead of XAML) by observing the CartesianChart.Content.LayoutUpdated event, but I recommend against this).
The recommended solution is to bind to the DataContext instead.
You can either set the UserControl itself as its own DataContext:
PeakrowChartGroupControl.xaml.cs
partial class PeakrowChartGroupControl : UserControl
{
public PeakrowChartGroupControl()
{
InitializeComponent();
this.DataContext = this;
}
}
PeakrowChartGroupControl.xaml
<UserControl x:Class="DryLab.Peakmovement.RetentionChartControl">
<Grid>
<lvc:CartesianChart x:Name="cc" Series="{Binding ChartParams.Seriescollection}">
<lvc:CartesianChart.AxisY>
<lvc:Axis Title="{Binding ChartParams.TitleAxY}"
LabelFormatter="{Binding ChartParams.Formatter}" />
</lvc:CartesianChart.AxisY>
<lvc:CartesianChart.AxisX>
<lvc:Axis Title="{Binding ChartParams.TitleAxX}"
Labels="{Binding ChartParams.Labels}" />
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
</Grid>
</UserControl>
Or alternatively e.g., in case the UserControl.DataContext is already set to something else, you can explicitly set the charts DataContext to e.g., the ChartParams property of RetentionChartControl:
PeakrowChartGroupControl.xaml
<UserControl x:Class="DryLab.Peakmovement.RetentionChartControl">
<Grid>
<lvc:CartesianChart DataContext="{Binding ChartParams, RelativeSource={RelativeSource AncestorType=local:RetentionChartControl}}"
Series="{Binding Seriescollection}">
<lvc:CartesianChart.AxisY>
<lvc:Axis Title="{Binding TitleAxY}"
LabelFormatter="{Binding Formatter}" />
</lvc:CartesianChart.AxisY>
<lvc:CartesianChart.AxisX>
<lvc:Axis Title="{Binding TitleAxX}"
Labels="{Binding Labels}" />
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
</Grid>
</UserControl>
Related
EDIT:
Here is a small sample Solution (VS 2013) showing my exact problem: nakkvarr.net/TestApp.zip
I have two views:
MainView
EmployeeView
I want my EmployeeView to reference the MainViewModel property "employees".
I tried this:
<DockPanel DataContext="{Binding DataContext.MainViewModel,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type EmployeeLisViewModel}}}">
But all I get is the Error: EmployeeListViewModel ist not supported in WPF ?!
(EmployeeListViewModel is also the name of the property of my MainViewModel)
The thing is:
My MainView has some menu items. One is for sorting the employee list, which is inside my UserControl. My UserControl ViewModel contains the Commands for sorting.
EDIT:
I changed it to vm:EmployeeListViewModel ... now the error is gone...but now I don't seem to get access to the ViewModel :/
<ia:InvokeCommandAction Command="{Binding LoadEmployeesCommand}"
Does not trigger, no emplyees get loaded. When I use the ViewModel directly, it works just fine.
MainWindow.xaml
<Window x:Class="de.XXX.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:de.XXX.Views"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
Icon="/Images/App.ico"
Style="{StaticResource MainWindowStyle}"
Title="MainWindow">
<DockPanel DataContext="{Binding MainViewModel, Source={StaticResource Locator}}">
<Menu DockPanel.Dock="Top">
...
</Menu>
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding EmployeeListViewModel}" />
</DockPanel>
</Window>
EmployeeListView.xaml //not model, copy & paste error x_X
<UserControl x:Class="de.XXX.Views.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="350">
<DockPanel DataContext="{Binding MainViewModel,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type vm:EmployeeListViewModel}}}">
<ListBox DockPanel.Dock="Bottom" x:Name="EmployeeList" ItemsSource="{Binding EmployeeList}" ScrollViewer.CanContentScroll="False" />
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadEmployeesCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
</DockPanel>
</UserControl>
If you modify your SubControl to below, this should work.
<Grid DataContext="{Binding Path=DataContext.SubVM, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
I have a hint on what you are trying to achieve and I guess you have missed some details on Binding especially on other underlying properties needed to be set. This part is working based on the given code. But then, there are numerous ways in making it better. Good luck.
I think there might be a combination of misunderstandings here! Your UserControl has
x:Class="de.XXX.Views.EmployeeListView"
defined as the class name in xaml. However, you're also saying that the file is named EmployeeListViewModel.xaml? A .xaml file should never be named a viewmodel. I believe you should have MainWindow.xaml, EmployeeListView.xaml, MainWindowViewModel.cs, and EmployeeListViewModel.cs, is this the case?
Also, in your MainWindow.xaml, you're already setting the datacontext of your EmployeeListView usercontrol:
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding EmployeeListViewModel}" />
This implies to me that your class structure is that MainViewModel contains an instance of the EmployeeListViewModel... is this true?
If all the above is true, then this part of your UserControl xaml does not make sense:
<DockPanel DataContext="{Binding MainViewModel,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type vm:EmployeeListViewModel}}}">
Whether EmployeeListViewModel is actually your view and not your viewmodel, the property "MainViewModel" does not exist on EmployeeListViewModel, so the binding will never work.
If you're trying to have a reference to your Window's DataContext, MainViewModel, within your UserControl, a possible way to do this is:
MainWindow.xaml:
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding}" />
This should set the DataContext of your EmployeeListView to MainViewModel. Then, in your EmployeeListView, you can do this:
<UserControl x:Class="de.XXX.Views.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="350">
<DockPanel
<ListBox DockPanel.Dock="Bottom" x:Name="EmployeeList" ItemsSource="{Binding EmployeeList}" ScrollViewer.CanContentScroll="False" />
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding EmployeeListViewModel.LoadEmployeesCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
</DockPanel>
</UserControl>
There's a lot of assumptions about your views and viewmodels I made for this answer. I hope this at least helps. If it doesn't make any sense, please post your viewmodels as well :)
I'm trying to use the RadDiagram commands from a separate window. My bindings don't work because "Cannot find source for binding with reference 'ElementName=Diagram'". My (simplified) XAML looks like this:
<UserControl x:Class="Client.Wpf.EditableLayoutControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:s="clr-namespace:Client.Wpf" xmlns:Converters="clr-namespace:Client.Wpf.Converters"
mc:Ignorable="d" DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300" d:DesignWidth="800" Background="Transparent" Name="Root">
<UserControl.Resources>
<Converters:MultiBoolToVisibilityConverter x:Key="multiBoolToVis"/>
<Converters:ObjectToBooleanConverter x:Key="objToBool"/>
<telerik:InvertedBooleanConverter x:Key="invBool"/>
</UserControl.Resources>
<telerik:RadDocking HasDocumentHost="False" BorderThickness="0" Name="DockingStation">
<telerik:RadSplitContainer>
<telerik:RadPaneGroup>
<telerik:RadPane CanDockInDocumentHost="False" Title="Controls" >
<telerik:RadPane.TitleTemplate>
<DataTemplate>
<Grid Margin="0 5">
<ContentPresenter Content="{Binding}" VerticalAlignment="Center"/>
<telerik:RadButton Command="telerik:DiagramCommands.Undo" CommandTarget="{Binding Path=Diagram, Source={RelativeSource FindAncestor, AncestorType={x:Type s:EditableLayoutControl}}}" />
</Grid>
</DataTemplate>
</telerik:RadPane.TitleTemplate>
<telerik:RadTabControl ...></telerik:RadTabControl>
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
<telerik:RadSplitContainer>
<telerik:RadPaneGroup>
<telerik:RadPane PaneHeaderVisibility="Collapsed" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<Grid>
<telerik:RadDiagram Name="Diagram">...
</telerik:RadDiagram>
</Grid>
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
</telerik:RadDocking>
</UserControl>
The problematic line of code is this beaut from inside the DataTemplate:
CommandTarget="{Binding Path=Diagram, Source={RelativeSource FindAncestor, AncestorType={x:Type s:EditableLayoutControl}}}"
That line of code doesn't work because Diagram is not a property, and bindings require properties. Using ElementName instead of Path there also doesn't work as ElementName and Source are exclusive. I also tried this:
CommandTarget="{Binding ElementName=Diagram}"
That line of code doesn't work. It gives this error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=Diagram'. BindingExpression:(no path); DataItem=null; target element is 'RadButton' (Name=''); target property is 'CommandTarget' (type 'IInputElement')
I also tried CommandTarget="{x:Reference Diagram}", but that feature is apparently not yet implemented in .NET 4.0. How is it done?
In this particular situation I had to set the Loaded event on the undo button. Then in my event handler (code-behind) I did this: ((RadButton)e.Source).CommandTarget = Diagram;.
I have a custom Tab Control that I have created, but I am having an issue. I have an Editable TextBox as part of the custom TabControl View.
<Controls:EditableTextControl x:Name="PageTypeName"
Style="{StaticResource ResourceKey={x:Type Controls:EditableTextControl}}" Grid.Row="0" TabIndex="0"
Uid="0"
AutomationProperties.AutomationId="PageTypeNameTextBox"
AutomationProperties.Name="PageTypeName"
Visibility="{Binding ElementName=PageTabControl,Path=ShowPageType}">
<Controls:EditableTextControl.ContextMenu>
<ContextMenu x:Name="TabContextMenu">
<MenuItem Header="Rename Page Type" Command="{Binding Path=PlacementTarget.EnterEditMode, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
AutomationProperties.AutomationId="RenamePageTypeMenuItem"
AutomationProperties.Name="RenamePageType"/>
<MenuItem Header="Delete Page Type" Command="{Binding Path=PageTypeDeletedCommand}"
AutomationProperties.AutomationId="DeletePageTypeMenuItem"
AutomationProperties.Name="DeletePageType"/>
</ContextMenu>
</Controls:EditableTextControl.ContextMenu>
<Controls:EditableTextControl.Content>
<!--<Binding Path="CurrentPageTypeViewModel.Name" Mode="TwoWay"/>-->
<Binding ElementName="PageTabControl" Path="CurrentPageTypeName" Mode ="TwoWay"/>
</Controls:EditableTextControl.Content>
</Controls:EditableTextControl>
In the Content section I am binding to a Dependency Prop called CurrentPageTypeName. This Depedency prop is part of this custom Tab Control.
public static DependencyProperty CurrentPageTypeNameProperty = DependencyProperty.Register("CurrentPageTypeName", typeof(object), typeof(TabControlView));
public object CurrentPageTypeName
{
get { return GetValue(CurrentPageTypeNameProperty) as object; }
set { SetValue(CurrentPageTypeNameProperty, value); }
}
In another view, where I am using the custom TabControl I then bind my property, with the actual name value, to CurrentPageTypeName property as seen below:
<Views:TabControlView Grid.Row="0" Name="RunPageTabControl"
TabItemsSource="{Binding RunPageTypeViewModels}"
SelectedTab="{Binding Converter={StaticResource debugConverter}}"
CurrentPageTypeName="{Binding Path=RunPageName, Mode=TwoWay}"
TabContentTemplateSelector="{StaticResource tabItemTemplateSelector}"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedTabIndex}"
ShowPageType="Hidden" >
<!--<Views:TabControlView.TabContentTemplate>
<DataTemplate DataType="{x:Type ViewModels:RunPageTypeViewModel}">
<RunViews:RunPageTypeView/>
</DataTemplate>
</Views:TabControlView.TabContentTemplate>-->
</Views:TabControlView>
My problem is that nothing seems to be happening. It is grabbing its Content from the Itemsource, and not from my chained Dependency props. Is what I am trying even possible? If so, what have I done wrong.
Thanks for looking.
Unless I'm missing something this is definitely possible. Here is a simplified working example.
User control with a dependency property named TestValue, containing a TextBox bound to this property:
<UserControl x:Class="TestApp.TestControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
x:Name="TestControlName">
<Grid>
<TextBox Text="{Binding ElementName=TestControlName, Path=TestValue, Mode=TwoWay}"/>
</Grid>
</UserControl>
A different view using this user control, binding the above mentioned dependency property to something:
<Window x:Class="TestApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestApp="clr-namespace:TestApp" Title="MainWindow"
Height="350" Width="525">
<StackPanel>
<TestApp:TestControl TestValue="{Binding ElementName=SourceTextBox, Path=Text, Mode=TwoWay}" />
<TextBox Name="SourceTextBox" />
</StackPanel>
</Window>
It sounds that the issue is somewhere in the part of the code you have not posted (e.g. wrong name used in Content binding).
I think you already solved this yourself for the "SelectedIndex" property. Just do the same thing for the "CurrentPageType" property i.e. use RelativeSource
I'm creating a UserControl I want to use something like this:
<controls:ColorWithText Color="Red" Text="Red color" />
So far, I've implemented similar controls like this:
<UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
<StackPanel Orientation="Horizontal" >
<Border Width="15" Height="15" Background="{Binding Color, ElementName=ThisControl}" />
<TextBlock Text="{Binding Text, ElementName=ThisControl}" />
</StackPanel>
</UserControl>
where Color and Text are dependency properties of the control defined in code. This works, but specifying ElementName every time seems unnecessary.
Another option that works is using
<UserControl x:Class=… DataContext="{Binding ElementName=ThisControl}" Name="ThisControl">
and not specifying ElementNames, but that doesn't seem like a clean solution to me either.
I have two questions:
Why doesn't <UserControl DataContext="{RelativeSource Self}"> work?
What is the best way to do something like this?
For first one, try :
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
And for second question, I think using ElementName or AncestorBinding is best way to bind to UserControl's properties.
Why can't you use <UserControl DataContext="{RelativeSource Self}">?
This is how you would use the control
<Grid DataContext="{StaticResource ViewModel}">
<!-- Here we'd expect this control to be bound to -->
<!-- ColorToUse on our ViewModel resource -->
<controls:ColorWithText Color="{Binding ColorToUse}" />
</Grid>
Now because we've hardcoded our data-context in the control it will instead attempt to lookup ColorToUse property on the ColorWithText object not your ViewModel, which will obviously fail.
This is why you can't set the DataContext on the user control. Thanks to Brandur for making me understand that.
What is the best way to do something like this?
Instead you should set the DataContext in the first child UI element in your control.
In your case you want
<StackPanel
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
Orientation="Horizontal" >
Now you have a DataContext which refers to your control so you can access any properties of that control using relative bindings.
I know this has been answered but none of the explanations give an Understanding of DataContext and how it works. This link does a great job for that.
EVERYTHING YOU WANTED TO KNOW ABOUT DATABINDING IN WPF, SILVERLIGHT AND WP7 (PART TWO)
In answer to your question #1
Why doesn't <UserControl DataContext="{RelativeSource Self}"> work?
This is a summary of the above link.
DataContext should not be set to Self at UserControl Element level. This is because it breaks the Inheritance of the DataContext. If you do set it to self and you place this control on a Window or another control, it will not inherit the Windows DataContext.
DataContext is inherited to all lower Elements of the XAML and to all the XAML of UserControls unless it is overwritten somewhere. By setting the UserControl DataContext to itself, this overwrites the DataContext and breaks Inheritance. Instead, nest it one Element deep in the XAML, in your case, the StackPanel. Put the DataContext binding here and bind it to the UserControl. This preserves the Inheritance.
See also this link below for a detailed explanation of this.
A SIMPLE PATTERN FOR CREATING RE-USEABLE USERCONTROLS IN WPF / SILVERLIGHT
In answer to your question #2
What is the best way to do something like this?
See code example below.
<UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=ThisControl}">
<Border Width="15" Height="15" Background="{Binding Color" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</UserControl>
Note that once you do this, you will not need the ElementName on each binding.
You should be using
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=Color}
for Databinding Related doubts always refer this sheet.
http://www.nbdtech.com/Blog/archive/2009/02/02/wpf-xaml-data-binding-cheat-sheet.aspx
You can set the datacontext to self at the constructor itself.
public ColorWithText()
{
InitializeComponent();
DataContext = this;
}
Now you can simply say
<UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
<StackPanel Orientation="Horizontal" >
<Border Width="15" Height="15" Background="{Binding Color}" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</UserControl>
For the desperate souls, who are trying to make pdross's answer work and can't:
It's missing an essential detail - Path=DataContext.
The lower code segment starts working when you add it there with this being the result:
<StackPanel DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext}">
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!
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>
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}}"