首页 > 解决方案 > 为 WPF TabControl 配置动态标题

问题描述

我正在尝试制作一个使用 WPF TabControl 允许用户添加新选项卡的基本窗口。我希望最终产品看起来有点像 Web 浏览器中选项卡的工作方式,其中最后一个选项卡只是一个“+”,单击它会添加一个新选项卡。

我正在尝试编写 XAML 代码来设置它,我发现我可以在“TabControl.Resources”中指定多个 DataTemplates,并且基于“DataType”,正确的 DataTemplate 将用于显示每个选项卡的正确视图.. . 但是在处理选项卡标题时,我只能为“TabControl.ItemTemplate”指定一个 DataTemplate

这是我到目前为止所拥有的:

<TabControl ItemsSource="{Binding Tabs}">
    <TabControl.Resources>

        <!-- If the tab is of type "TabViewModel" I want this content -->
        <DataTemplate DataType="x:Type vm:TabViewModel">
            <!-- TabView is defined as a separate user control -->
            <v:TabView/>
        </DataTemplate>

        <!-- If the tab is of type "NewTabViewModel" I want this content -->
        <DataTemplate DataType="x:Type vm:NewTabViewModel">
            <!-- NewTabView is defined as a separate user control -->
            <v:NewTabView/>
        </DataTemplate>
    </TabControl.Resources>

    <TabControl.ItemTemplate>
        <!-- if the tab is of type "TabViewModel" I want this header -->
        <DataTemplate DataType="x:Type vm:TabViewModel">
            <TextBlock Text="{Binding Name}"/>
        </DataTemplate>

        <!-- If the tab is of type "NewTabViewModel" I want this header -->
        <!-- ERROR: Adding a second "DataTemplate" here results in an error -->
        <DataTemplate DataType="x:Type vm:NewTabViewModel">
            <TextBlock Text="+"/>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

谷歌搜索,我发现了一些关于设置 TemplateSelector 和编写一堆背景 C# 代码的文章,但对于这么简单的事情来说,这似乎太过分了。如果它是常规 TabViewModel 对象,我只希望它显示选项卡名称,如果它是 NewTabViewModel 对象,则显示“+”。

标签: c#wpfxamltabcontrol

解决方案


这里有两种完全不同的 DataTemplate 用途。

ItemTemplate 属性属于 DataTemplate 类型。具体来说,一个 DataTemplate,而不是一个集合。它期望您将其设置为您需要的任何模板,这就是它将用于填充选项卡标题的内容。此外,在 TabControl 的情况下,该模板将应用于所有标题;你不能在每个标签的基础上改变它。

选项卡面板本身是 ContentControl 类型,每个面板都绑定到一个视图模型。ContentControl 包含一个 ContentPresenter,它遍历逻辑树,为其绑定的数据类型查找 DataTemplate(在内部,DataTemplate 的 DataType 属性只是将类型本身设置为 x:Key 的语法糖)。

您的问题是您尝试像 ResourceDictionary 一样使用 ItemTemplate,指定多个 DataTemplates,而它希望您提供模板本身,并且只提供一个。所以要实现你所追求的,你需要做的就是给它一个 DataTemplate 并用一个 ContentPresenter 填充它(正如 Dreamer 所建议的那样),就像选项卡面板本身一样。此 ContentPresenter 有自己的 ResourceDictionary,您可以在其中放置标题模板:

<TabControl ItemsSource="{Binding Tabs}">

    <TabControl.Resources>

        <!-- Panel templates -->

        <DataTemplate DataType="{x:Type vm:TabViewModel}">
            <v:TabView />
        </DataTemplate>

        <DataTemplate DataType="{x:Type vm:NewTabViewModel}">
            <v:NewTabView />
        </DataTemplate>
    </TabControl.Resources>

    <TabControl.ItemTemplate>
        <DataTemplate>
            <ContentPresenter Content="{Binding}">
                <ContentPresenter.Resources>

                    <!-- Header templates -->

                    <DataTemplate DataType="{x:Type vm:TabViewModel}">
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>

                    <DataTemplate DataType="{x:Type vm:NewTabViewModel}">
                        <TextBlock Text="+"/>
                    </DataTemplate>

                </ContentPresenter.Resources>
            </ContentPresenter>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

推荐阅读