Reputation: 33
Quite new to WPF and MVVM and I'm trying to bind a ContentTemplate (or ItemTemplate, neither have worked) to a DataTemplate property in a C# WPF program. I'm doing this because I have a config file that defines different "entry display types" for each "entry" in an attempt to not have to make countless views/viewmodels (right now, there is only one generic entry viewmodel that keeps track of the label, data, and display type and I'd prefer to keep it that way to avoid unnecessary bloat of the class structure). Is there any way to make this work?
This is example of one of the things I have tried:
XAML:
<ItemsControl IsTabStop="False" ItemsSource="{Binding Path=FNEntries}"Margin="12,46,12,12">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl ContentTemplate="{Binding Path=TypeView}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
CS (inside the entry view model class constructor which has (DataTemplate)TypeView and (string)PropertyName):
NodeTypeView = (DataTemplate)Application.Current.FindResource("TypeTest");
Resource XAML:
<DataTemplate x:Key="TypeTest">
<TextBlock Margin="2,6">
<TextBlock Text="{Binding Path=PropertyName}" />
</TextBlock>
When I run with that, nothing shows up. However, if I put the contents of the resource data template directly in place of the content control, things show up just fine (except that it isn't data-driven in the way I want). Any help/advice would be greatly appreciated. Thanks!
Upvotes: 1
Views: 5005
Reputation: 6651
I'd genuinely say you are mostly doing it wrong =)
Having templates stored in the ViewModel is generally a bad idea, because you would be storing graphical objects in your VM. This should be done ll on the View side
If you want a variable DataTemplate according to the type of your items or whatever, here are a few alternative, "cleaner" solutions:
Prerequisite: All of your Templates are defined as resource somewhere.
Let's say you have a ResourceDictionary
somewhere with the following, for test purposes:
<DataTemplate x:Key="Template1" />
<DataTemplate x:Key="Template2" />
<DataTemplate x:Key="Template3" />
Solution 1 : use ItemTemplateSelector
(cleanest solution imho) For this matter, I'd redirect you to this excellent tutorial which taught me how to use it If I could understand it, no way you can't =D
Solution 2 : use a converter in your Binding
Let's slightly change your Binding
, by making it binding on the current object itself, with a converter
<DataTemplate>
<ContentControl ContentTemplate="{Binding Converter={StaticResource MyConverter}}" />
</DataTemplate>
Here is what your converter would look like (note: the value
object here is the bound object, in your case you are working with its type, so this example is about Types as well)
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.GetType() == typeof(WhateverYouWant))
{
return (DataTemplate)Application.Current.FindResource("OneTemplate");
}
else if (value.getType() == typeof(AnotherTypeHere))
{
return (DataTemplate)Application.Current.FindResource("AnotherTemplate");
}
// other cases here...
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value; //We don't care about this!
}
}
This would do the trick for you
I guess both of these solutions would work, and are cleaner, but beware that this is the exact aim of ItemTemplateSelector
. The Converter
approach is the one I used bfore I knew about those template selectors, I don't use it anymore
Cheers!
Upvotes: 5