【问题标题】:UWP DataTemplates for multiple item types in ListViewListView 中多种项目类型的 UWP DataTemplates
【发布时间】:2015-09-05 14:32:14
【问题描述】:

我将如何实施?

假设这是我的模型:

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 在必要时实现,并且视图模型在页面中已正确初始化。

如何使用自己对应的 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 类型名称并使用反射来调用类型,主要是因为我不想混合CLR 和 XML 命名空间出现在 XAML 中。如果你能找到一种使用 XML 命名空间调用类型的方法,我很乐意将其作为答案。

【问题讨论】:

  • 您是否尝试过 TargetType="{x:Type model:Dog}" 因为该属性的类型是“Type”而不是字符串。
  • 我做到了。 UWP 不再支持 x:Type 标记。那将是 WPF 的解决方案。 :)
  • 为什么不在选择模板方法中创建条件语句,而不是使用 TemplateMatch?
  • 我很想找到一个可以重复使用的解决方案。在 SelectTemplate 中选择特定类型违反了这个想法。这就是我希望能够在 XAML 中定义可用类型的原因。
  • 我现在也遇到了同样的问题,你找到解决办法了吗?

标签: xaml listview win-universal-app datatemplate


【解决方案1】:

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

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

【讨论】:

  • @Nick 的回答与这个类似(@NekitoSP 的回答)。但我喜欢这个比较类型而不是比较全名字符串的方式。
【解决方案2】:

提示在错误信息中。

无法从文本创建“Ui.Components.TemplateMatch” '模型:狗'

请注意,'model:Dog' 作为文本而不是类型出现在您的选择器中。

将您的 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>

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

【讨论】:

  • 这是一种可能的解决方案,但我真的很想绑定到Type 类型的属性。你知道,就像普通的 DataTemplates 一样。此外,当我在 XAML 中时,在一个地方使用 CLR 命名空间并在另一个地方使用 XML 命名空间并不是很直接。我知道这是在挑剔解决方法,但我想找到完美的答案。
猜你喜欢
  • 1970-01-01
  • 2018-12-09
  • 1970-01-01
  • 2023-04-09
  • 2016-11-16
  • 1970-01-01
  • 2019-01-15
  • 1970-01-01
  • 2017-10-02
相关资源
最近更新 更多