用于ListView中多种项目类型的UWP DataTemplates [英] UWP DataTemplates for multiple item types in ListView
问题描述
我将如何实施呢?
让我们说这是我的模特:
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" });
}
}
为使该问题中的代码量更多,请假定在必要时实现了INotifyPropertyChanged
,并且已在页面中正确初始化了ViewModel.
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.
如何使用我自己的对应DataTemplates?在WPF中,我只需要定义多个没有x:Key
但具有定义的DataType的DataTemplates,然后让ListView根据项的类型选择要使用的模板. UWP不喜欢这样.编译器只是声明Dictionary Item "DataTemplate" must have a Key attribute
.那我该如何实现我的目标呢?
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?
我当前的尝试是创建自定义DataTemplateSelector
,这似乎很简单.
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; }
}
像这样在XAML中定义它:
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>
不幸的是,当我运行此代码时,在运行时会发生异常,指出Failed to create a 'Ui.Components.TemplateMatch' from the text 'model:Dog'.
,因此绑定到Type
属性似乎并不那么容易.
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.
感谢您的帮助!
请注意,我想使用类型Type
的属性,而不是string
,在该属性中我将传递CLR类型名称并使用反射来调用该类型,主要是因为我不想混合使用CLR和XML名称空间出现在XAML中.如果您找到使用XML名称空间调用该类型的方法,我将很乐意将其作为答案.
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.
推荐答案
我找到了解决方法.如果您可以创建这些类型的实例-可以将其用于检测类型:
I found workaround. If you able to create instances of these types - you can use it for detecting types:
[ContentProperty(Name = nameof(Matches))]
public class TypeTemplateSelector : DataTemplateSelector
{
public ObservableCollection<TemplateMatch> Matches { get; set; }
public TypeTemplateSelector()
{
this.Matches = new ObservableCollection<TemplateMatch>();
}
protected override DataTemplate SelectTemplateCore(object item)
{
return this.Matches.FirstOrDefault(m => m.ItemOfType.GetType().Equals(item.GetType()))?.TemplateContent;
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return this.Matches.FirstOrDefault(m => m.ItemOfType.GetType().Equals(item.GetType()))?.TemplateContent;
}
}
[ContentProperty(Name = nameof(ItemOfType))]
public class TemplateMatch
{
public object ItemOfType { get; set; }
public DataTemplate TemplateContent { get; set; }
}
XAML:
<controls:TypeTemplateSelector>
<controls:TemplateMatch TemplateContent="{StaticResource FishTemplate}">
<models:Fish/>
</controls:TemplateMatch>
<controls:TemplateMatch TemplateContent="{StaticResource DogTemplate}">
<models:Dog/>
</controls:TemplateMatch>
</controls:TypeTemplateSelector>
这篇关于用于ListView中多种项目类型的UWP DataTemplates的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!