Configuring dynamic headers for a WPF TabControl

牧云@^-^@ 提交于 2021-02-11 14:00:36

问题


I am trying to make a basic window that uses a WPF TabControl to allow users to add new tabs. I want the end product to look somewhat like the way tabs work in a web browser where the last tab is just a "+" that when clicked it will add a new tab.

I am trying to write the XAML code to set this up and I found that I can specify multiple DataTemplates within "TabControl.Resources" and based on the "DataType" the correct DataTemplate will be used to display the correct view for each tab... but when dealing with the tab headers I can only specify a single DataTemplate for "TabControl.ItemTemplate"

This is what I have so far:

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

Googling around I found some articles on setting up a TemplateSelector and writing a bunch of background C# code, but that seems drastically overkill for something this simple. I just want it to display the tab name if it is a regular TabViewModel object and a "+" if it is a NewTabViewModel object.


回答1:


There's two completely different uses of DataTemplate here.

An ItemTemplate property is of type DataTemplate. Specifically, one DataTemplate, not a collection. It's expecting you to set that to whatever template you need, and that's what it will use to populate the tab header. Furthermore, in the case of TabControl, that's the template that will get applied to all headers; you don't get to change it on a per-tab basis.

The tab panels themselves are of type ContentControl, with each one bound to a view model. A ContentControl contains a ContentPresenter, which traverses the logical tree looking for a DataTemplate for the data type that it has been bound to (internally, the DataTemplate's DataType property is just syntactic sugar for setting the type itself as the x:Key).

Your problem is that you're trying to use the ItemTemplate like a ResourceDictionary, specifying multiple DataTemplates, when it's expecting you to provide the template itself, and only one. So to implement what you're after, all you need to do is give it a DataTemplate and populate it with a ContentPresenter (as suggested by Dreamer), the same as the tab panel itself has. This ContentPresenter has its own ResourceDictionary, and that's where you can put your header templates:

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


来源:https://stackoverflow.com/questions/61532527/configuring-dynamic-headers-for-a-wpf-tabcontrol

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!