UWP DataTemplates用于ListView中的多个项类型

Dan*_*mid 8 xaml listview datatemplate win-universal-app

我将如何实现这一目标?

让我们说这是我的模型:

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" });
    }
}
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题中的代码量,请假设INotifyPropertyChanged在必要时实现,并且ViewModel在页面中正确初始化.

我如何使用自己的相应DataTemplates?在WPF中,我只是定义多个DataTemplates而不x:Key使用定义的DataType,并让ListView根据项目的类型选择使用哪个.UWP不喜欢这样; 编译器简单说明Dictionary Item "DataTemplate" must have a Key attribute.那么我该如何实现目标呢?

目前的尝试

我目前的尝试是做一个习惯DataTemplateSelector,这似乎很直接.

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; }
}
Run Code Online (Sandbox Code Playgroud)

像这样在XAML中定义它:

<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>
Run Code Online (Sandbox Code Playgroud)

不幸的是,当我运行它时,在运行时发生异常,说明Failed to create a 'Ui.Components.TemplateMatch' from the text 'model:Dog'.所以它似乎绑定到Type属性并不那么容易.

任何帮助表示赞赏!

请注意,我想使用类型的属性Type,而不是string传递CLR类型名称和使用反射来调用类型的属性,主要是因为我不希望在XAML中出现混合CLR和XML命名空间.如果您可以找到使用XML命名空间调用类型的方法,我很乐意将其作为答案.

Nek*_*oSP 3

我找到了解决方法。如果您能够创建这些类型的实例 - 您可以使用它来检测类型:

[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; }
}
Run Code Online (Sandbox Code Playgroud)

XAML:

<controls:TypeTemplateSelector>
    <controls:TemplateMatch TemplateContent="{StaticResource FishTemplate}">
        <models:Fish/>
    </controls:TemplateMatch>
    <controls:TemplateMatch TemplateContent="{StaticResource DogTemplate}">
        <models:Dog/>
    </controls:TemplateMatch>
</controls:TypeTemplateSelector>
Run Code Online (Sandbox Code Playgroud)