ListView

时间:2015-09-05 14:32:14

标签: xaml listview win-universal-app datatemplate

我将如何实施此目标?

让我们说这是我的模特:

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。

如何使用我自己的相应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; }
}

在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>

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

感谢任何帮助!

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

2 个答案:

答案 0 :(得分:2)

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

[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>

答案 1 :(得分:0)

线索在错误信息中。

  

无法创建&#39; Ui.Components.TemplateMatch&#39;从文本   &#39;模型:狗&#39;

注意&#39;模型:Dog&#39;作为文本而不是类型来到你的选择器。

将TemplateMatch类的TargetType属性更改为字符串,而不是像这样的类型: -

public class TemplateMatch
{
    public string TargetType { get; set; }
    public DataTemplate Template { get; set; }
}

然后将模板选择器类更改为

public class MyDataTemplateSelector : DataTemplateSelector
{
    public ObservableCollection<TemplateMatch> Matches { get; set; }

    public MyDataTemplateSelector()
    {
        Matches = new ObservableCollection<TemplateMatch>();
    }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        return Matches.FirstOrDefault(m => m.TargetType.Equals(item.GetType().ToString()))?.Template;
    }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        return Matches.FirstOrDefault(m => m.TargetType.Equals(item.GetType().ToString()))?.Template;
    }
}

最后将你的xaml改为阅读

<ListView ItemsSource="{x:Bind ViewModel.Animals}">
    <ListView.ItemTemplateSelector>
        <cmp:MyDataTemplateSelector>
            <cmp:MyDataTemplateSelector.Matches>
                <cmp:TemplateMatch TargetType="YourFullNamespaceNotXamlNamespace.Dog" Template="{StaticResource DogTemplate}"/>
                <cmp:TemplateMatch TargetType="YourFullNamespaceNotXamlNamespace.Fish" Template="{StaticResource FishTemplate}"/>
            </cmp:MyDataTemplateSelector.Matches>
        </cmp:MyDataTemplateSelector>
    </ListView.ItemTemplateSelector>
</ListView>

重点是忘记尝试将其作为类型传递给选择器,并将typename作为字符串传递(完整命名空间而不是Xaml命名空间)。