How would I go about implementing this?
Let's say this is my model:
public interface IAnimal
{
string Name { get; }
}
public class Fish : IAnimal
{
public string Name { get; set; }
public int ScalesCount { get; set; }
}
public class Dog : IAnimal
{
public string Name { get; set; }
public string CollarManufacturerName { get; set; }
}
public class ViewModel
{
public ObservableCollection<IAnimal> Animals { get; set; }
public ViewModel()
{
this.Animals = new ObservableCollection<IAnimal>();
this.Animals.Add(new Fish { Name = "Carl", ScalesCount = 9000 });
this.Animals.Add(new Dog { Name = "Fifi", CollarManufacturerName = "Macrosoft" });
}
}
For the sake of the amount of code in this question please assume that INotifyPropertyChanged
is implemented where necessary, and that the ViewModel is correctly initialized in the page.
How can I use my own corresponding DataTemplates? In WPF I would just define multiple DataTemplates without an x:Key
but with a defined DataType and let the ListView chose which to use based on the type of the item. UWP doesn't like that; the compiler simply states Dictionary Item "DataTemplate" must have a Key attribute
. So how do I accomplish my goal?
Current Attempt
My current attempt is to make a custom DataTemplateSelector
, which seems rather straight forward.
public class MyDataTemplateSelector: Windows.UI.Xaml.Controls.DataTemplateSelector
{
public ObservableCollection<TemplateMatch> Matches { get; set; }
public DataTemplateSelector()
{
this.Matches = new ObservableCollection<TemplateMatch>();
}
protected override DataTemplate SelectTemplateCore(object item)
{
return this.Matches.FirstOrDefault(m => m.TargetType.Equals(item))?.Template;
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return this.Matches.FirstOrDefault(m => m.TargetType.Equals(item))?.Template;
}
}
public class TemplateMatch
{
public Type TargetType { get; set; }
public DataTemplate Template { get; set; }
}
Define it in XAML like this:
<ListView ItemsSource="{x:Bind ViewModel.Animals}">
<ListView.ItemTemplateSelector>
<cmp:MyDataTemplateSelector>
<cmp:MyDataTemplateSelector.Matches>
<cmp:TemplateMatch TargetType="model:Dog" Template="{StaticResource DogTemplate}"/>
<cmp:TemplateMatch TargetType="model:Fish" Template="{StaticResource FishTemplate}"/>
</cmp:MyDataTemplateSelector.Matches>
</cmp:MyDataTemplateSelector>
</ListView.ItemTemplateSelector>
</ListView>
Unfortunately when I run this, an Exception occurs during runtime, stating Failed to create a 'Ui.Components.TemplateMatch' from the text 'model:Dog'.
So it seems binding to a Type
property is not that easy.
Any help is appreciated!
Please note that I'd like to use a property of type Type
, as opposed to string
where I would pass the CLR type name and using reflection to invoke the type, mostly because I don't want mixed CLR and XML namespaces appear in XAML. If you can find a way to invoke the type using the XML namespace, I'll gladly take that as an answer.
The clue is in the error message.
Note the 'model:Dog' is coming to your selector as text not a type.
Change your TemplateMatch class TargetType property to string instead of type like this:-
Then change your template selector class to read
Finally change your xaml to read
The point is to forget trying to pass it to your selector as a type, and pass the typename as a string instead (Full namespace not Xaml namespace).
I found workaround. If you able to create instances of these types - you can use it for detecting types:
XAML: