I have read nearly a thousand posts explaining that setting a closed generic type as DataType
on a DataTemplate
does not work, because WPF wouldn't support that. But as a matter of fact, this is just wrong.
I can define the following DataTemplate
in my Window.Resources
and it will be used when I assign a list of strings to a content control. For example:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type TypeName=Generic:List`1[System.String]}">
<TextBlock Text="Hi List of Strings"
FontSize="40"
Foreground="Cyan"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl x:Name="_contentControl">
</ContentControl>
</Grid>
</Window>
and in code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_contentControl.Content = new List<string> { "Huhu" };
}
}
With this setup you will see "Hi List of Strings". For me that's the proof that I can define generic types as DataType
. But I want to take it one step further: I'd like to define a Dictionary<string, string>
as DataType
. But unfortunately, I can't get it to work.
So the question is: How can I define a Dictionary<string, string>
as DataType
of a DataTemplate
?
If you know the answer, you can stop reading. But since it is good practice to show what I already did, I keep writing. What did I do already? At first I went brute-force and tried several combinations similar to:
- DataType="{x:Type TypeName=Generic:Dictionary`2[System.String];[System.String]}"
- DataType="{x:Type TypeName=Generic:Dictionary`2[System.String],[System.String]}"
- DataType="{x:Type TypeName=Generic:Dictionary`2[System.String,System.String]}"
But since none of them worked, I dove into System.Xaml
and looked at TypeExtension
, GenericTypeNameParser
and GenericTypeNameScanner
, because I thought that these are the codelines which resolve the type. But looking at the code I realized that ` is an invalid character.
To prove it, I wrote my own MarkupExtension
public class UseTheTypeExtensionsParser : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
var a = new TypeExtension("Generic:List`1[[System.String]]");
var type = a.ProvideValue(serviceProvider);
return type.ToString();
}
}
and used it as follows:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
xmlns:WpfApp1="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ContentControl Content="{WpfApp1:UseTheTypeExtensionsParser}"/>
</Grid>
</Window>
And this threw the exception that the character ` was not expected and that the XAML-type is invalid.
That got me wondering why my first example worked. I think, that on markup-compiling the XAML for WPF, it is not the TypeExtension
that is used for resolving the XamlType, but i think that the XamlNamespace
is used. Because this class has the MangleGenericTypeName
-method which uses the `-character.
But I still can't see the code which extracts the type arguments, so I cannot see the correct syntax to specify the type arguments for the Dictionary. This is where I am stuck.
(Needless to say that the Microsoft-Docs are worthless on this topic.)
Edit: Since it seems unclear why I want this, I will explain it: I want the automatic selection of a ContentTemplate
of the ContentControl
. And of course: my constructed DataTemplate
in the example is very simple. But everyone should be able to imagine, that I want different DataTemplates for Lists, for Dictionaries or for simple strings.
I have a ViewModel which has a public object Result { get; }
Property. And sometimes, the result is an int, sometimes a string, sometimes a List and so on and so forth. I am binding this Result
-property to the Content
-Property of a ContentControl
. And for all the types mentioned, I wrote different DataTemplates which are automatically selected by WPF. So int
s are shown in a Rectangle
and String
s are shown in an Ellipse
.
After I got all this to work, I want another DataTemplate
, but this time for a Dictionary
.
List<string>
. A generic type would beList<T>
. You can't use generic types simply because the compiler has no way of knowing what's included in those generic types – InconsequentialList<SomeEntity>
or anObservableCollection<SomeOtherEntity>
or something returned by EF – InconsequentialDataType
in a DataTemplate. WPF data binding works with reflection so you don't need to specify the type as long as the object the template binds to has properties that satisfy the binding expressions. – InconsequentialList<string>
and create a non-generic type which closesList<T>
. Some example: https://mcmap.net/q/271299/-how-to-reference-a-generic-type-in-the-datatype-attribute-of-a-datatemplate. And if almost all tutorials and docs show binding to generic types anyway, why don't you just answer the question? – Louque