在GridViewColumn的CellTemplate中为每种数据类型创建一个DataTemplate。

18

我有一个ObservableCollection,其中包含多种类型的视图模型,我希望为每个GridViewColumn的CellTemplates中的每个类型创建一个DataTemplate。在这个简单的示例中,我可以创建一个基本的ViewModel,但我希望能够仅从xaml中实现这一点。下面的xaml显示了我正在尝试做的事情,其中一个DataTemplates将用于每个CellTemplate。

如果有一个GridViewColumn.Resources,我会在那里定义DataTemplates,然后在CellTemplate中使用ContentPresenter来使用DataTemplate,但我显然不能这样做。我想我可能需要一个TemplateSelector,但我不确定从哪里开始。

<ListView ItemsSource={Binding GenericObservableCollection>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Type">
                <GridViewColumn.CellTemplate>
                    <DataTemplate DataType="{x:Type vm:ActionInputViewModel}">
                        <TextBlock Text="Input"/>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type vm:ActionOutputViewModel}">
                        <TextBlock Text="Output"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Value">
                <GridViewColumn.CellTemplate>
                    <DataTemplate DataType="{x:Type vm:ActionInputViewModel}">
                        <TextBlock Text="{Binding Property1}"/>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type vm:ActionOutputViewModel}">
                        <TextBlock Text="{Binding Property2}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>
1个回答

38

这里有几种不同的方法可供选择。您可以编写一个DataTemplateSelector,并将其分配给GridViewColumn.CellTemplateSelector属性:

public class ViewModelTemplateSelector : DataTemplateSelector
{
    public DataTemplate InputTemplate { get; set; }
    public DataTemplate OutputTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        return (item is ActionInputViewModel) ? InputTemplate : OutputTemplate;
    }
}

然后,您可以将所有的模板移动到某个资源中 - 在这里我只是为了简洁把它放在了ListView中:

    <ListView ItemsSource="{Binding GenericObservableCollection}">
        <ListView.Resources>
            <DataTemplate x:Key="InLabel" DataType="{x:Type vm:ActionInputViewModel}">
                <TextBlock Text="Input"/>
            </DataTemplate>
            <DataTemplate x:Key="OutLabel" DataType="{x:Type vm:ActionOutputViewModel}">
                <TextBlock Text="Output"/>
            </DataTemplate>
            <DataTemplate x:Key="InValue" DataType="{x:Type vm:ActionInputViewModel}">
                <TextBlock Text="{Binding Property1}"/>
            </DataTemplate>
            <DataTemplate x:Key="OutValue" DataType="{x:Type vm:ActionOutputViewModel}">
                <TextBlock Text="{Binding Property2}"/>
            </DataTemplate>
        </ListView.Resources>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Type">
                    <GridViewColumn.CellTemplateSelector>
                        <vm:ViewModelTemplateSelector InputTemplate="{StaticResource InLabel}" OutputTemplate="{StaticResource OutLabel}"/>
                    </GridViewColumn.CellTemplateSelector>
                </GridViewColumn>
                <GridViewColumn Header="Value">
                    <GridViewColumn.CellTemplateSelector>
                        <vm:ViewModelTemplateSelector InputTemplate="{StaticResource InValue}" OutputTemplate="{StaticResource OutValue}"/>
                    </GridViewColumn.CellTemplateSelector>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>

如果您希望全部保留在XAML中,您可以依靠数据类型来为您解析正确的模板。通常,您只需将它们放入最近的容器的资源集合中,但不幸的是,GridViewColumn不是一个UI元素,因此没有资源集合。您可以通过为每个单元格添加ContentControl来解决此问题,该控件可以拥有自己的类型化模板:

    <ListView ItemsSource="{Binding GenericObservableCollection}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Type">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <ContentControl Content="{Binding}">
                                <ContentControl.Resources>
                                    <DataTemplate DataType="{x:Type vm:ActionInputViewModel}">
                                        <TextBlock Text="Input"/>
                                    </DataTemplate>
                                    <DataTemplate DataType="{x:Type vm:ActionOutputViewModel}">
                                        <TextBlock Text="Output"/>
                                    </DataTemplate>
                                </ContentControl.Resources>
                            </ContentControl>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="Value">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <ContentControl Content="{Binding}">
                                <ContentControl.Resources>
                                    <DataTemplate DataType="{x:Type vm:ActionInputViewModel}">
                                        <TextBlock Text="{Binding Property1}"/>
                                    </DataTemplate>
                                    <DataTemplate DataType="{x:Type vm:ActionOutputViewModel}">
                                        <TextBlock Text="{Binding Property2}"/>
                                    </DataTemplate>
                                </ContentControl.Resources>
                            </ContentControl>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>

不管哪种方式,都应该给你相同的结果。


1
非常好。感谢您为我提供了我所考虑的两种方法的详细答案。 - scuba88
天啊,我折腾了好几个小时,差点就成功了,但你做到了。我的错误在于绑定了ContentControlDataContext,而不是像你正确指出的那样绑定Content。赞美和感谢。 - Glenn Slayden
我喜欢看到人们真正花时间回答问题。从你提供的细节来看,这一点显而易见。干得好。+1。 - cplotts
需要注意的是:在第一种方法中使用CellTemplateSelector时,不要像在ListView的其他列中那样设置DisplayMemberBinding属性。 - schoetbi

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