WPF中的条件列表项模板或数据模板

22

这可能是一个显而易见的问题,但我认为实现它可能有多种方法,因此这不仅对我有用,希望对其他人也有用。

本质上,我正在寻找实现可接受不同类型对象并使用相应的项/数据模板呈现它们的列表视图的最佳方法。

例如...我们有一个标准产品列表视图,当查看不同类别时,业务已决定为每个不同类别显示不同的项目模板样式。

在这里提出这个问题的主要原因是避免使用丑陋的hacky解决方案,而是发现一个好的清洁方法。

希望我提供了足够的信息,如果需要更多信息,请告诉我。

2个回答

37

Resources中只需使用相应的DataType指定DataTemplates即可,例如:

<ListView ItemsSource="{Binding Data}">
    <ListView.Resources>
        <!-- Do NOT set the x:Key -->
        <DataTemplate DataType="{x:Type local:Employee}">
            <TextBlock Text="{Binding Name}" Foreground="Blue"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Machine}">
            <TextBlock Text="{Binding Model}" Foreground="Red"/>
        </DataTemplate>
    </ListView.Resources>
</ListView>

屏幕截图

(请注意,DataTemplate.DataType 也可用于隐式 XML 数据模板化(请参阅文档),因此该属性类型不是 System.Type,所以与 Style.TargetType 不同,您必须使用 x:Type 引用 CLR 类型。如果您只输入字符串,它将不会转换为类型。)

您可能还想了解CompositeCollections,以获得干净的合并列表,其中包含各种类型。


我使用的示例数据:

ObservableCollection<Employee> data = new ObservableCollection<Employee>(new Employee[]
{
    new Employee("Hans", "Programmer")      ,
    new Employee("Elister", "Programmer")   ,
    new Employee("Steve", "GUI Designer")   ,
    new Employee("Stephen", "GUI Designer") ,
    new Employee("Joe", "Coffee Getter")    ,
    new Employee("Julien", "Programmer")    ,
    new Employee("John", "Coffee Getter")   ,
});
ObservableCollection<Machine> data2 = new ObservableCollection<Machine>(new Machine[]
{
    new Machine("XI2",    String.Empty),
    new Machine("MK2-xx", String.Empty),
    new Machine("A2-B16", String.Empty),
});
CompositeCollection cc1 = new CompositeCollection();
cc1.Add(new CollectionContainer() { Collection = data });
cc1.Add(new CollectionContainer() { Collection = data2 });
Data = cc1;

谢谢,太好了。如果你有一个非常大的不同项目集,你能想到一种使它更清晰的方法吗? - ocodo
你的意思是什么?"unclean"是什么意思? - H.B.
如果你有非常多的项目,那么在你的模板中会有非常多的DataTemplate节点列表,我想知道是否有一种策略来管理这个维护问题。(不用太担心,这只是我的假设情况,我是为了以后参考而问的) - ocodo
你可以将其拆分并重构为多个ResourceDictionaries,然后在某个时候合并它们,但我以前没有遇到过这样的问题,所以无法给出明确的建议。 - H.B.
代码复制后进行修改,但是没有显示任何内容(只有空的ListView)。我是否遗漏了什么? - javajavajavajavajava
@javajavajavajavajava:你是否熟悉数据绑定?因为你不能只是复制我的代码然后期望它能工作。(还请看:DataContext) - H.B.

13

一个选择是在您的代码中创建DataTemplateSelector

public class QueueDisplayDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {

        var listBoxItem = item as JobQueueListBoxItem;
        var resourceName = String.Empty;
        switch (listBoxItem.JobQueueListBoxItemType)
        {
            case JobQueueListBoxItemType.QueuedJob :
                resourceName = "DataTemplateQueuedJob";
                break;
            case JobQueueListBoxItemType.TransferWorker :
                resourceName = "DataTemplateTransferWorker";
                break;
            default:
                throw new InvalidOperationException(string.Format("There is no corresponding list box template for {0}", listBoxItem.JobQueueListBoxItemType));
        }
        var element = container as FrameworkElement;
        return element.FindResource(resourceName) as DataTemplate;
    }
}
这将被声明为 XAML 资源,然后可以在 XAML 中使用。
        <ResourceDictionary>
            <local:QueueDisplayDataTemplateSelector x:Key="QueueDisplayDataTemplateSelector" />

然后将这个赋值给您的列表框:

    <ListBox ItemsSource="{Binding ListBoxContents}" 
             ItemTemplateSelector="{StaticResource QueueDisplayDataTemplateSelector}"
             >

是的,这可能是我会入手的方式,我认为 x:type 更加简洁,但两者在大量备选项的情况下都有些受影响。 - ocodo
5
这并不一定是黑客行为,“DataTemplateSelector”是一个“正式”的属性,有其存在的理由。如果您只是基于类型进行区分,那么它就是多余的,但是使用选择器,您可以将不同的模板应用于相同类型的对象。 - H.B.
为了证明使用“hack”这个词的合理性,我是因为使用case而不是使用多态来管理模板选择。 - ocodo
我怀疑 WPF 的内部机制除了查看类型并从字典中获取相应的 DataTemplate 外,不会执行任何其他操作,毕竟数据对象上没有附加继承方法可以提供它。它并没有太大的不同,但我猜它有较少的错误空间。 - H.B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接