I'm writing a custom DataTemplateSelector
for a ComboBox
control and I'll need to use it to display different DateTemplates
for different kind of objects, in both the closed and open modes for the ComboBox
.
Here's the DataTemplateSelector
I came up with:
public class ComboBoxTypedDataTemplateSelector : DataTemplateSelector
{
public IEnumerable<DataTemplate> SelectedTemplates { get; set; }
public IEnumerable<DataTemplate> DropDownTemplates { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
IEnumerable<DataTemplate> source = container.FindParent<ComboBoxItem>() == null
? SelectedTemplates // Get the template for the closed mode
: DropDownTemplates; // Get the template for the open UI mode
Type type = item.GetType();
return null; // Some LINQ to get the first DataTemplate in source with the {x:DataType} that equals type
}
}
public sealed class DataTemplatesCollection : List<DataTemplate> { }
And here's how I'd use it in XAML:
<ComboBox>
<mvvm:ComboBoxTypedDataTemplateSelector>
<mvvm:ComboBoxTypedDataTemplateSelector.SelectedTemplates>
<mvvm:DataTemplatesCollection>
<DataTemplate x:DataType="models:SomeType">
<TextBlock Text="{x:Bind ...}"/>
</DataTemplate>
<DataTemplate x:DataType="models:SomeOtherType">
<TextBlock Text="{x:Bind ...}"/>
</DataTemplate>
</mvvm:DataTemplatesCollection>
</mvvm:ComboBoxTypedDataTemplateSelector.SelectedTemplates>
<mvvm:ComboBoxTypedDataTemplateSelector.DropDownTemplates>
<mvvm:DataTemplatesCollection>
<DataTemplate x:DataType="models:SomeType">
<TextBlock Text="{x:Bind ...}"/>
</DataTemplate>
<DataTemplate x:DataType="models:SomeOtherType">
<TextBlock Text="{x:Bind ...}"/>
</DataTemplate>
</mvvm:DataTemplatesCollection>
</mvvm:ComboBoxTypedDataTemplateSelector.DropDownTemplates>
</mvvm:ComboBoxTypedDataTemplateSelector>
</ComboBox>
Now, the only piece of the puzzle I'm missing, I can't figure out how to get that {x:DataType} property in C# (I know it's not actually a real property, but I hope there's a way to retrieve it via code).
I need something like that to be able to get the right DataTemplate
for each object, from the right templates group.
Is there a way I can achieve that?
NOTE: I know I could just write a specific DataTemplateSelector
that has the hardcoded names of the templates to return for each item type, and I can use that method as a fallback option. But, I was wondering if it was possible to write a more generic selector with this approach in order to make it more modular and be able to reuse it in the future.
Thanks for your help!
EDIT: following the suggestion by Vincent, I wrote an attached property to store a given Type
in a DataTemplate
:
public class DataTypeHelper
{
public static Type GetAttachedDataType(DataTemplate element)
{
return (Type)element.GetValue(AttachedDataTypeProperty);
}
public static void SetAttachedDataType(DataTemplate element, Type value)
{
element.SetValue(AttachedDataTypeProperty, value);
}
public static readonly DependencyProperty AttachedDataTypeProperty =
DependencyProperty.RegisterAttached("AttachedDataType", typeof(Type), typeof(DataTypeHelper), new PropertyMetadata(default(Type)));
}
And I've tried to use it like this:
...
<DataTemplate x:DataType="someXlmns:SomeClass"
mvvm:DataTypeHelper.AttachedDataType="someXlmns:SomeClass">
...
</DataTemplate>
But I'm getting a XamlParseException
at the line where I set the attached property to my type. I've tried to set that property to "Grid" (just as a test) and it doesn't crash, I don't understand why isn't it working with my custom type.
EDIT #2: looks like the x:Type markup extension is not available in UWP and I couldn't find another way (I don't think it's possible at all) to get a Type instance directly from XAML, so I had to just use the type name in XAML and then compare it to item.GetType().Name
in the template selector.
The ability to assign a Type property directly from XAML would have been better as it'd also would have had syntax/spell-check in the XAML designer, but at least this approach works fine.