WPF TabControl 和数据模板
我有一组 ViewModel,我将它们绑定到 TabControl 的 ItemsSource 属性。我们将这些 ViewModel 称为 AViewModel、BViewModel 和 CViewModel。其中每一个都需要有一个不同的 ItemTemplate(用于标题;因为它们每个都需要显示不同的图标)和不同的 ContentTemplate(因为它们有非常不同的交互模型)。
我想要的是这样的:
在 Resource.xaml 文件中的某处定义:
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type CViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type CViewModel}">
...
</DataTemplate>
单独定义:
<TabControl ItemTemplate="[ Some way to select "ItemTemplate" based on the type ]"
ContentTemplate="[ Some way to select "ContentTemplate" based on the type ]"/>
现在,我知道实际上,每次我使用相同的密钥定义 DataTemplate 时,系统都会抱怨。但是,我可以做一些与此类似的事情,让我根据名称和数据类型将 DataTemplate 放入 TabControl 中吗?
I've got a set of ViewModels that I'm binding to the ItemsSource property of a TabControl. Let's call those ViewModels AViewModel, BViewModel, and CViewModel. Each one of those needs to have a different ItemTemplate (for the header; because they each need to show a different icon) and a different ContentTemplate (because they have very different interaction models).
What I'd like is something like this:
Defined in Resource.xaml files somewhere:
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type CViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type CViewModel}">
...
</DataTemplate>
Defined separately:
<TabControl ItemTemplate="[ Some way to select "ItemTemplate" based on the type ]"
ContentTemplate="[ Some way to select "ContentTemplate" based on the type ]"/>
Now, I know that realistically, each time I define a DataTemplate with the same key the system is just going to complain. But, is there something I can do that's similar to this that will let me put a DataTemplate into a TabControl based on a name and a DataType?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
最简单的方法是使用自动模板系统,将 DataTemplates 包含在 ContentControl 的资源中。模板的范围仅限于它们所在的元素!
The easiest way would be to use the automatic template system, by including the DataTemplates in the resources of a ContentControl. The scope of the templates are limited to the element they reside within!
您可以删除 x:Key :) 当遇到给定类型时,这将自动应用模板(在我看来,这可能是 WPF 最强大且未充分利用的功能之一。
这篇 WPF 博士文章很好地介绍了 DataTemplates。您可以参考的部分需要注意的是“为给定的 CLR 数据类型定义默认模板”
。 /EntryID/24/Default.aspx" rel="noreferrer">http://www.drwpf.com/blog/Home/tabid/36/EntryID/24/Default.aspx
如果这没有帮助根据您的情况,您可能可以使用样式(ItemContainerStyle)做一些接近您正在寻找的事情,并使用数据触发器根据类型设置内容和标题,
下面的示例取决于您的 ViewModel 具有名为“的属性”。类型”的定义非常像这样(如果你有一个基本的 ViewModel,很容易放入一个基本 ViewModel):
所以只要你有它,这应该允许你做任何你想做的事情。注意我在这里的文本块中有“一个标题!” ,但这很容易是任何东西(图标等)。
我在这里有两种方式......一种样式应用模板(如果您已经在这些方面进行了大量投资),另一种样式只是使用设置器将内容移动到正确的位置。
HTH,安德森
You can remove the x:Key :) This will automatically apply the template when the given type is encountered (probably one of the most powerful and underused features of WPF, imo.
This Dr. WPF article goes over DataTemplates pretty well. The section you'll want to pay attention to is "Defining a Default Template for a Given CLR Data Type".
http://www.drwpf.com/blog/Home/tabid/36/EntryID/24/Default.aspx
If this doesn't help your situation, you might be able to do something close to what you are looking for using a Style (ItemContainerStyle) and setting the content and header based on the type using a data trigger.
The sample below hinges on your ViewModel having a property called "Type" defined pretty much like this (easily put in a base ViewModel if you have one):
So as long as you have that, this should allow you to do anything you want. Note I have "A Header!" in a textblock here, but that could easily be anything (icon, etc).
I've got it in here two ways... one style applies templates (if you have a significant investment in these already) and the other just uses setters to move the content to the right places.
HTH, Anderson
一种方法是使用 DataTemplateSelector 并让每个选择器从单独的 ResourceDictionary 解析资源。
One way would be to use
DataTemplateSelector
s and have each one resolve the resource from a separateResourceDictionary
.在此示例中,我在
TabControl
的资源部分中为要在选项卡项中显示的每个视图模型使用 DataTemplates。在本例中,我将
ViewModelType1
映射到View1
,将ViewModelType2
映射到View2
。视图模型将自动设置为视图的 DataContext 对象。
为了显示选项卡项目标题,我使用
ItemTemplate
。我绑定到的视图模型具有不同的类型,但派生自具有
Title
属性的公共基类ChildViewModel
。因此,我可以设置一个绑定来拾取标题以将其显示在选项卡项标题中。此外,我在选项卡项标题中显示一个“关闭”按钮。如果您不需要,只需从示例代码中删除该按钮,这样您就只有标题文本。
该模板使用 Content="{Binding}" 在内容控件中显示视图。
选项卡项目的内容使用简单的
ItemTemplate
呈现, 包含选项卡控件具有ContainerViewModel
类型的容器视图模型作为DataContext
。这里我收集了选项卡控件中显示的所有视图模型。我还有当前选定的视图模型(选项卡项)的属性。这是我的容器视图模型的缩短版本(我跳过了更改通知部分)。
In this example I use DataTemplates in the resources section of my
TabControl
for each view model I want to display in the tab items.In this case I map
ViewModelType1
toView1
andViewModelType2
toView2
.The view models will be set as
DataContext
object of the views automatically.For displaying the tab item header, I use an
ItemTemplate
.The view models I bind to are of different types, but derive from a common base class
ChildViewModel
that has aTitle
property. So I can set up a binding to pick up the title to display it in the tab item header.In addition I display a "Close" Button in the tab item header. If you do not need that, just remove the button from the example code so you just have the header text.
The contents of the tab items are rendered with a simple
ItemTemplate
which displays the view in a content control with Content="{Binding}".The user control which contains the tab control has a container view model of type
ContainerViewModel
asDataContext
. Here I have a collection of all the view models displayed in the tab control. I also have a property for the currently selected view model (tab item).This is a shortened version of my container view model (I skipped the change notification part).
Josh Smith 在他的优秀文章和示例项目 采用模型-视图-视图模型设计模式的 WPF 应用。在这种方法中,由于 VM 集合中的每个项目都有一个相应的 DataTemplate,将视图链接到 VM 类型(通过省略 x:Key,正如 Anderson Imes 正确指出的那样),因此每个选项卡可以具有完全不同的 UI。有关详细信息,请参阅完整文章和源代码。
XAML 的关键部分是:
有一个缺点 - 如果选项卡中的 UI 很大/很复杂,因此绘制速度很慢(例如,具有大量数据的数据网格),从 ItemsSource 驱动 WPF TabControl 会出现性能问题。有关此问题的更多信息,请在 SO 中搜索“WPF VirtualizingStackPanel 以提高性能”。
Josh Smith uses exactly this technique (of driving a tab control with a view model collection) in his excellent article and sample project WPF Apps With The Model-View-ViewModel Design Pattern. In this approach, because each item in the VM collection has a corresponding DataTemplate linking the View to the VM Type (by omitting the x:Key as Anderson Imes correctly notes), each tab can have a completely different UI. See the full article and source code for details.
The key parts of the XAML are:
There is one downside - driving a WPF TabControl from an ItemsSource has performance issues if the UI in the tabs is big/complex and therefore slow to draw (e.g., datagrids with lots of data). For more on this issue, search SO for "WPF VirtualizingStackPanel for increased performance".