Include shared resource in ResourceDictionaries for multiple controls - wpf

I have a DataTemplate I'd like to use with multiple ContentControls. But each of these controls needs to contain additional DataTemplates as well, and the final list is different for each one. Something like this:
<DataTemplate x:Key="FooDataTemplate" DataType="{x:Type Foo}" />
<!-- snip -->
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="{x:Type Bar}" />
<!-- Also want a copy of FooDataTemplate here, but without an explicit key -->
</ContentControl.Resources>
</ContentControl>
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="{x:Type Baz}" />
<!-- Also want a copy of FooDataTemplate here, but without an explicit key -->
</ContentControl.Resources>
</ContentControl>
Is there a XAML syntax that will let me do this? I could create an "AddResourceToDictionary" attached behavior or put FooDataTemplate in its own resource dictionary file and merge it in, but both seem like an awful lot of hassle for a simple thing...

You could add FooDataTemplate to the ContentControl's ResourceDictionary, provided that it's in scope:
<ContentControl>
<ContentControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type Bar}" />
<StaticResource ResourceKey="FooDataTemplate" />
</ResourceDictionary>
</ContentControl.Resources>
</ContentControl>
But if you want a "copy of FooDataTemplate, but without an explicit key", you need to create a new DataTemplate yourself. There is no XAML syntax that lets you base a DataTemplate on another one. A template must be defined as a whole. So I guess the answer to your question is simply no.
FooDataTemplate can only be referenced and used as-is. And since it's already in scope, it doesn't really make any sense adding it to the ContentControl's ResourceDictionary.

Related

Sharing DataTemplates between controls

I have a ContentPresenter in a couple of places in my application with exactly the same DataTemplates. For now I simply copy-pasted them, but I'd like to clean that up and share them between ContentPresenter instances. I tried this approach:
<ContentControl Content="{Binding DataEditorViewModel}">
<ContentControl.Resources>
<ResourceDictionary Source="pack://application:,,,/LogAnalyzer;component/PredicateDataEditors.xaml" />
</ContentControl.Resources>
</ContentControl>
Application runs, but DataTemplates aren't being applied, I simply see name of class being ContentPresenter's content instead of defined template. I put templates in ResourceDictionary in the following way:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LogAnalyzer"
xmlns:p="clr-namespace:LogAnalyzer.BusinessLogic.ViewModels.Processing;assembly=LogAnalyzer.BusinessLogic"
xmlns:xwt="http://schemas.xceed.com/wpf/xaml/toolkit">
<DataTemplate DataType="{x:Type p:MessageRuleDataEditorViewModel}" x:Key="{x:Type p:MessageRuleDataEditorViewModel}">
(...)
</DataTemplate>
(...)
</ResourceDictionary>
What should I do to embed DataTemplates correctly in ContentPresenter's resources?
if you define this data template in your app.xaml:
<DataTemplate DataType="{x:Type p:MessageRuleDataEditorViewModel}">
<TextBlock Text="testing" />
</DataTemplate>
and then do something like:
<ListBox ItemsSource="{Binding MyCollectionOfMessageRuleDataEditorViewModels}"/>
then your data template is automatically applied to every object of that same type. dont define a key for your global templates

Styles on DataTemplates

I have an issue with using Styles on DataTemplates in WPF ... It appears to suck. Suppose that you define a DataTemplate:
<DataTemplate DataType="{x:Type local:DataSource}">
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding Path=myData}" />
</DataTemplate>
Is there now any way to dynamically style this element? (eg. change the background color in some parts of the application) My problems are:
if you set a style in some parent-control, it gets ignored since there is already a style in the datatemplate
if you set a property on a parent-control, it doesn't get inherited since styles have precedence over property inheritence
Does anyone see a way to do this?
you can try it using DynamicResource
<DataTemplate DataType="{x:Type local:DataSource}">
<TextBox Style="{DynamicResource TextBoxStyle}" Text="{Binding Path=myData}" />
</DataTemplate>
and then later if you need to change the style in any other control. you can just declare same resource with same key for that control. Suppose you are using it in ListBox.
<ListBox>
<ListBox.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<!--define changed style.-->
</Style>
</ListBox.Resources>
</ListBox>
new style will apply over there.

WPF Implicit selection of template using DataTemplate, but outside of 'List'

In my project, I have TreeView, which contains a tree of objects of various types (all subclassed from the same superclass).
To the right of my TreeView I would like to have a "panel" (at the moment I just have a Grid) which displays information about the object currently selected in the tree. I want to use DataTemplate, as in the second example on this page, to adapt the layout & content of my "panel" based on the subclass type; however, I cannot find a suitable container (as I don't want a list control - I want to change my display for one item based on the selection in the treeview).
This question asks the same thing but I don't think the answer is suitable for me because I want the template to change dynamically depending on the type.
I.e. I was hoping for something like:
<[A Suitable Container] Margin="189,39,12,12" DataContext="{Binding ElementName=treeView1, Path=SelectedItem}">
<DataTemplate DataType="{x:Type local:subclass1}">
<Grid>
<!-- subclass1 specific stuff -->
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:subclass2}">
<Grid>
<!-- subclass2 specific stuff -->
</Grid>
</DataTemplate>
</[A Suitable Container]>
Use a ContentControl
<ContentControl Content="{Binding ElementName=treeView1, Path=SelectedItem}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>

Is there a way to use data-template inheritance in WPF?

Is it possible to have DataTemplate composition or inheritance (similar to "BasedOn" in Styles)? There are 2 instances where I need that.
For inherited classes: I have a base class with several inherited classes. I don't want to duplicate the base class template in each of the derived class's DataTemplate.
Different Views: for the same class I want to define a datatemplate, and then add to that template as appropriate. Ex. the base template will display the data in the object and then i want different templates that can perform different actions on the object, while displaying the data (inheriting the base template).
The only thing that I have found do to for this kind of thing is this:
<DataTemplate x:Key="BaseClass">
<!-- base class template here -->
</DataTemplate>
<DataTemplate DataType="{x:Type app:BaseClass}">
<ContentPresenter Content="{Binding}"
ContentTemplate="{StaticResource BaseClass}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type app:DerivedClass}">
<StackPanel>
<ContentPresenter Content="{Binding}"
ContentTemplate="{StaticResource BaseClass}"/>
<!-- derived class extra template here -->
</StackPanel>
</DataTemplate>
Basically this creates a "common" template that can be referenced using a key (BaseClass in this case). Then we define the real DataTemplate for the base class, and any derived classes. The derived class template would then add it's own "stuff".
There was some discussion about this on msdn a while back, but no one came up with a better solution that I saw.
#Fragilerus and #Liz, actually I think I did come up with something better. Here's another approach that not only avoids the extra ContentPresenter binding, but also removes the need to have to apply a template within a template since the shared content is direct content which is set at compile-time. The only thing that happens at run-time would be the bindings you set inside the direct content. As such, this greatly speeds up the UI when compared to the other solution.
<!-- Content for the template (note: not a template itself) -->
<Border x:Shared="False"
x:Key="Foo"
BorderBrush="Red"
BorderThickness="1"
CornerRadius="4">
<TextBlock Text="{Binding SomeProp}" />
</Border>
<DataTemplate x:Key="TemplateA">
<!-- Static resource - No binding needed -->
<ContentPresenter Content="{StaticResource Foo}" />
</DataTemplate>
<DataTemplate x:Key="TemplateB">
<!-- Static resource - No binding needed -->
<ContentPresenter Content="{StaticResource Foo}" />
</DataTemplate>
Important: Make sure to use the x:Shared attribute on your shared content or this will not work.
The WPF'y Way
The above said, this really isn't the most WPF-friendly way to do what you're after. That can be achieved using the DataTemplateSelector class which does exactly that... select's a data template based on whatever criteria you set.
For instance, you could easily set one up that looks for your known data types and returns the same DataTemplate for both of them, but for all other types, it falls back to the system to resolve the DataTemplate. That's what we actually do here.
Hope this helps! :)

How to Automatically Use a DataTemplate Based on ContentControl's Current Content's DataType

When I attempt to specify multiple DataTemplates for use by a ContentControl so that the correct one (based on Type) is used, I end up with Content that is simply the Content's ToString() value.
<ContentControl DataContext="{Binding MyTreeRootViewModels}" Content="{Binding /, Path=CurrentlySelectedTreeViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="x:Type vm:TypeAViewModel">
<StackPanel>
<local:TypeAUserControl />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="x:Type vm:TypeBViewModel">
<StackPanel>
<local:TypeBUserControl />
</StackPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
In the example above I would see MyApp.ViewModel.TypeAViewModel displayed when a tree node of TypeAViewModel is returned by CurrentlySelectedTreeViewModel. I expect to see my TypeAViewModelUserControl.
I've tried putting a single <TextBlock Text="TESTING"/> element in one of my data templates just to see if the problem was related to my user controls. Same result.
Any ideas what I am doing wrong?
(By the way, the CurrentlySelectedTreeViewModel is a property that returns the currently selected node in my TreeView. It seems to work just fine - as I select nodes in the tree, the correct type name for the node appears ContentControl).
The x:Type bit should be between curly braces {}:
<DataTemplate DataType="{x:Type vm:TypeAViewModel}">
x:Type is a MarkupExtension, which requires {} to indicate to the XAML compiler.

Resources