Using TemplateBinding inside DataTemplates - wpf

I'm creating a custom control, and I'm having problems binding UI elements inside a DataTemplate to the custom control's dependency properties.
There's a content control in my control that should change its content and content template according to a certain property, so I bound it this way -
<ContentControl Content="{TemplateBinding ControlMode}" ContentTemplateSelector="{StaticResource TemplateSelector}"/>
The Content Template selector is defined this way -
<ns:TemplateSelector x:Key="TemplateSelector">
<ns:TemplateSelector.Template1>
<DataTemplate>
<TreeView ItemsSource="{TemplateBinding TreeSource}"/>
</DataTemplate>
</ns:TemplateSelector.Template1>
<ns:TemplateSelector.Template2>
<DataTemplate>
<ListView ItemsSource="{TemplateBinding ListSource}"/>
</DataTemplate>
</ns:TemplateSelector.Template2>
</ns:TemplateSelector>
The problem is that the TreeView and the ListView can't be bound to their itemssource with TemplateBinding due to this error for example -
"Cannot find TreeSourceProperty on the type ContentPresenter"
I've been looking around for an answer and I found this answer that simple states that this is impossible.
How to use template binding inside data template in custom control (Silverlight)
So if this really is impossible, how else could I bind the elements inside my template to the DependencyProperties of the CustomControl?
Thanks!

In WPF you can use a binding with RelativeSource targeting the "templated" control.
e.g.
{Binding TreeSource,
RelativeSource={RelativeSource AncestorType=MyCustomControl}}
Edit: If you have a break in a tree you could possibly work around that by passing that control around, e.g.
<ControlThatOwnsPopup
Tag="{Binding RelativeSource={RelativeSource AncestorType=MyCustomControl}}">
<Popup>...
<TreeView ItemsSource="{Binding PlacementTarget.Tag.TreeSource,
RelativeSource={RelativeSource AncestorType=Popup}}"/>

Related

Josh Smith's legendary article: I need a bit more on the DataBinding that takes place

It’s about the MSDN article of Josh Smith on MVVM and the sample application. I know there are lots of question on SO about this topic, and I’ve explored them all. Most of them are focused on MVVM, but my issue is, I think, more XAML related than MVVM.
The sample application contains the following XAML for the Workspaces area -
<Border Grid.Column="2" Style="{StaticResource MainBorderStyle}">
<ContentControl Content="{Binding Path=Workspaces}" ContentTemplate="{StaticResource ResourceKey=WorkspacesTemplate}"/>
</Border>
and the related resources are -
//Explains how to render the 'Workspace' content area in the main window
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl Margin="4" ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"
ItemTemplate="{StaticResource ResourceKey=ClosableTabItemTemplate}"/>
</DataTemplate>
//Explains how to render a tab item with a close button
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel Width="120">
<Button Command="{Binding Path=CloseCommand}" Content="X" Cursor="Hand"
DockPanel.Dock="Right" Focusable="False" FontFamily="Courier"
FontSize="9" FontWeight="Bold" Margin="0,1,0,0" Padding="0"
VerticalContentAlignment="Bottom" Width="16" Height="16"/>
<ContentPresenter Content="{Binding Path=DisplayName}" VerticalAlignment="Center"/>
</DockPanel>
</DataTemplate>
What I still don't understand -
The syntax ItemsSource="{Binding}" should bind the ItemsSource directly to the TabControl’s DataContext, rather than to any of the DataContext’s properties. But where exactly is the TabControl’s DataContext being set?
How exactly is Content="{Binding Path=Workspaces}" creating a binding between ItemsSource of the TabControl and the Workspaces (the ObservableCollection of WorkspaceViewModel)?
The article says By relying on data binding, the Content property of a TabItem receives a ViewModelBase-derived object to display. How ?!? Ok, through data binding. But that is just too much to abstract away, for me.
In general I’m missing the way binding is flowing/working through these two resources behind the scene to load the Views in the TabItems. To me it's like, what is causing what to be bound to what.
This legendary article and the sample application is something extremely useful for WPF/MVVM beginners. But it’s not much elaborative. I myself have learned using MVVM with this one. I think there is and will be some others like me. So, can anyone explain the binding sequences a little bit more elaborately please?
Relevant Note :
May be it'll give you a hint of what I already know in this context, and help you in answering. I'm a beginner level WPF application developer. With my not so good knowledge on XAML -
I’m aware of the magic through typed-DataTemplate of displaying the View when the ViewModel type occurs, and then setting the ViewModel as the DataContext of that View
My understanding is, Content tells what to display on a ContentControl and ContentTemplate tells how to display that Content.
I have a little more than basic data binding concept, and I've worked in some WPF/MVVM projects.
The syntax ItemsSource="{Binding}" should bind the ItemsSource
directly to the TabControl’s DataContext, rather than to any of the
DataContext’s properties. But where exactly is the TabControl’s
DataContext being set?
By virtue of being a data template, the TabControl will have its DataContext set (by WPF) to the data item it is templating. In fact, the root-level item in the data template will have its DataContext set by WPF. In this case, it's a TabControl.
Since the data template is assigned to the ContentControl's ContentTemplate property, it will automatically receive the ContentControl's Content as its DataContext.
How exactly is Content="{Binding Path=Workspaces}" creating a binding
between ItemsSource of the TabControl and the Workspaces (the
ObservableCollection of WorkspaceViewModel)?
I think my previous answer addresses this, but let me re-state it in a form that directly answers this question. The binding on the ContentControl ensures that the DataContext for the DataTemplate is set to the workspace collection. Thus, the TabControl can bind to the workspace collection by specifying a Binding without a Path.
The article says By relying on data binding, the Content property of a
TabItem receives a ViewModelBase-derived object to display. How ?!?
Ok, through data binding. But that is just too much to abstract away,
for me.
Again, this comes down to WPF automatically assigning the correct object as the data context of the generated item (a TabItem in this case). When an ItemsControl (such as TabControl) is bound to a collection, it generates a container (a TabItem in this case) for each item in the bound collection. The container automatically receives the data item (a workspace view model in this case) as its data context.
"But where exactly is the TabControl’s DataContext being set?" Workspaces is the DataContext and so the Itemssource for the TabControl.
<ContentControl Content="{Binding Path=Workspaces}" ContentTemplate="{StaticResource ResourceKey=WorkspacesTemplate}"/>
"How exactly is Content="{Binding Path=Workspaces}" creating a binding between ItemsSource of the TabControl and the Workspaces (the ObservableCollection of WorkspaceViewModel)?" Workspaces are the DataContext for the DataTemplate/TabControl and the ItemsSource is set to the DataContext.
<TabControl Margin="4" ItemsSource="{Binding}"
"The article says By relying on data binding, the Content property of a TabItem receives a ViewModelBase-derived object to display. How ?!?" i dont know the article, but i assume that Workspaces is a collection of object derived from ViewModelBase. every tabitem represent on item from the collection and so a viewmodelbase-derived object.

DataGrid binding in DataTemplate

I'm currently trying to do some binding inside of a datagrid but I'm having problems getting up to the level of DataContext of the view.
Here is the code:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Operators}"
ItemsSource="{Binding DataContext.OperatorList,ElementName=FilterGrid}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
Any ideas on whats wrong? The View's Viewmodel is connected in the code behind.
EDIT: The Binding that is not working is the ItemsSource binding shown above
When you use the DataTemplate of the DataGrid, you cannot use ElementName bindings as it won't resolve properly due to limitations in the resolution capabilities of FindControl within the DataGrid control hierarchy. You need to use a RelativeSource binding that travels up the control tree looking for a specific control type (which you need to determine - from your element name I assumed it was a DataGrid ancestor type).
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
SelectedItem="{Binding Operators}"
ItemsSource="{Binding DataContext.OperatorList,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
See this SO post that shares some potentially related sample code using MVVM to access the DataContext of the UserControl host to populate a ComboBox ItemsSource.

How to expose ItemTemplate and ContentTemplate from a UserControl

I have written a Well control, similar to the Visual Studio editor tabs so that a user can have multiple documents open and can see one or more at a time. It is derived from a UserControl and exposes an ObservableCollection of OpenDocuments that binds to the ViewModel. If I were to implement this as a simple TabControl, then this would be how it would look:
<TabControl
Grid.Row="1"
Grid.Column="1"
ItemsSource="{Binding OpenDocuments}"
SelectedItem="{Binding SelectedTab, Mode=TwoWay}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<vw:DocumentView />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This gives me Name in the tabitem header and the DocumentView (another user control in the Content area).
My control has a ContentTemplate but it is of course representing the whole of the control so all I get to see is the DocumentView. My control doesn't have an ItemTemplate.
How do I expose an ItemTemplate and ContentTemplate?
Andrew
EDIT -------------------------------------------------------------------------
Thanks for the replies. It looks like this:
A user can have one or more document wells holding one or more tabs. All the consumer has access to is the list of visible tabs and the currently selected tabitem.
Notice that the tabs are all empty! I don't understand how to specify the ContentTemplate for the <vw:DocumentView> in the same way as the TabControl example above.
Andrew
I am a little confused on exactly what your control looks like but here is what I think you are after.
If your control can inherit from ItemsControl you will get the ItemTemplate property already defined for you. All you have to do in your control template is to Template bind the ItemsControl property to the correct place in the template.
The same applies for the ContentTemplate. If you have the a ContentTemplate property already defined for you control you just need to Template Bind it to the correct place in the control.
It would look something like this
Control Consumer
<MyControlNamespace:MyControl ContentTemplate="{StaticResource MyContentTemplate}" ItemTemplate="{StaticResource MyItemTemplate}" />
Implementation of "MyControl" from above (totally pseudo code)
<MyControl>
<ItemsPresenter ItemTemplate="{TemplateBinding ItemTemplate}" />
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" />
</MyControl>
Again, my example doesn't fully make sense but I am not sure what your control looks like and not entirely sure what you are asking, but hopefully this helps.

Silverlight RelativeSource of TemplatedParent Binding within a DataTemplate, Is it possible?

I'm trying to make a bar graph usercontrol. I'm creating each bar using a DataTemplate.
The problem is in order to compute the height of each bar, I first need to know the height of its container (the TemplatedParent). Unfortunately what I have:
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height, Converter={StaticResource HeightConverter}, Mode=OneWay}"
does not work. Each time a value of NaN is returned to my Converter. Does RelativeSource={RelativeSource TemplatedParent} not work in this context? What else can I do to allow my DataTemplate to "talk" to the element it is being applied to?
Incase it helps here is the barebones DataTemplate:
<DataTemplate x:Key="BarGraphTemplate">
<Grid Width="30">
<Rectangle HorizontalAlignment="Center" Stroke="Black" Width="20" Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height, Converter={StaticResource HeightConverter}, Mode=OneWay}" VerticalAlignment="Bottom" />
</Grid>
</DataTemplate>
To answer your question RelativeSource only works in a ControlTemplate it doesn't work in a DataTemplate.
Is there a reason why the Silverlight Toolkit Chart controls don't work for you in creating a bar graph (or a Column Chart as the Tookit refers to vertical set of bars).
Have you tried the ActualHeight property? It should return you a value. RelativeSource with the TemplatedParent mode will work in a data template, but it will return the content presenter of the templated control/item, not the control/item itself (which it does when used in a control template). To experiment, put a button in the data template, and assign that binding expression (without the path) to its Tag property. Handle its Click event, and put a breakpoint in the event handler. Now when you run the project and click on the button, the breakpoint will be hit in your code, and you can see the object that it is binding to from the Tag property of the button (which you can see from the sender parameter). Hope this helps...

WPF: How do I use two different controls in an ItemsControl?

I have a WPF ItemsControl who's ItemsSource is bound to an observable collection of view models in MVVM. The ItemTemplate is set to the user control that I want. However, there are instances when I would like another control instead of the one specified in XAML.
How can I easily do this?
Use DataTemplates to map view models to views:
<ItemsControl ItemsSource="{Binding SomeCollectionOfViewModels}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:FirstViewModel}">
<Label>Foo</Label>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SecondViewModel}">
<Label>Bar</Label>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
If I understand you have a collection that contains two different type of object and you want 2 different template.
You could build a datatemplate for each object type and leave WPF to render the right template based on the object type.

Resources