c# - 为 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 对象,则显示“+”。
解决方案
这里有两种完全不同的 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>
推荐阅读
- google-apps-script - 谷歌表函数 myfunction() 解释了吗?
- azure - Azure DevOps 构建 YML 不是条件不工作
- node.js - Macbook m1节点js docker镜像构建失败
- ruby-on-rails - 使用 ActiveRecord 锁定回调内的关联?
- javascript - 在 JS 中编辑 JSON 并将其放入数组中
- filter - 过滤不带双引号的ansible输出参数
- python - 如果 python 日志处理程序引发异常会发生什么?
- forms - 如何直接从图表生成谷歌表单(或类似的东西)?
- django - 如何用pythonanywhere配置django项目的sqlite
- node.js - Node.js 包发布到 github,^ 类型依赖没有解决