WPF的TabControl和的DataTemplates(WPF TabControl and D

2019-08-03 00:16发布

我有一组我绑定到一个TabControl的ItemsSource属性的ViewModels的。 让我们把那些的ViewModels 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的?

Answer 1:

一种方法是使用DataTemplateSelector S和各有一个解决资源从一个单独ResourceDictionary



Answer 2:

最简单的方法是使用自动模板系统,通过在一个ContentControl中的资源的DataTemplates。 模板的范围仅限于它们所在内的元素!

<TabControl ItemsSource="{Binding TabViewModels}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding}">
                <ContentControl.Resources>
                    <DataTemplate DataType="{x:Type AViewModel}">
                        ...
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type BViewModel}">
                        ...
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type CViewModel}">
                        ...
                    </DataTemplate>
                </ContentControl.Resources>
            </ContentControl>
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type AViewModel}">
            ...
        </DataTemplate>
         <DataTemplate DataType="{x:Type BViewModel}">
            ...
        </DataTemplate>
        <DataTemplate DataType="{x:Type CViewModel}">
            ...
        </DataTemplate>
    </TabControl.Resources>
</TabControl>


Answer 3:

您可以删除X:关键:)当给定类型遇到(可能是WPF的最强大的和未充分利用的功能之一,国际海事组织这将自动应用模板。

这WPF博士本文将详细的DataTemplates相当不错。 你要注意部分“ 定义默认模板对于给定的CLR数据类型 ”。

http://www.drwpf.com/blog/Home/tabid/36/EntryID/24/Default.aspx

如果这没有帮助你的情况,你也许能够做一些接近你在找什么用样式(ItemContainerStyle)以及基于使用数据触发类型的内容和标题。

下面的示例取决于其定义非常喜欢这样(轻松的放进一个基础视图模型,如果你有一个)被称为“类型”属性的视图模型:

public Type Type 
{ 
   get { return this.GetType(); } 
}

所以只要你有,这应该让你做任何你想要的。 注意:我有“头!” 在这里一个文本块,但很容易被任何东西(图标等)。

我在这里两个方面得到了它......一个样式适用模板(如果你有这些已经是显著投资)和其他只使用setter方法的内容移动到正确的地方。

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300"
        xmlns:local="clr-namespace:WpfApplication1">
    <Window.Resources>
        <CompositeCollection x:Key="MyCollection">
            <local:AViewModel Header="A Viewmodel" Content="A Content" />
            <local:BViewModel Header="B ViewModel" Content="B Content" />
        </CompositeCollection>

    <DataTemplate x:Key="ATypeHeader" DataType="{x:Type local:AViewModel}">
        <WrapPanel>
            <TextBlock>A Header!</TextBlock>
            <TextBlock Text="{Binding Header}" />
        </WrapPanel>
    </DataTemplate>
    <DataTemplate x:Key="ATypeContent" DataType="{x:Type local:AViewModel}">
        <StackPanel>
            <TextBlock>Begin "A" Content</TextBlock>
            <TextBlock Text="{Binding Content}" />
        </StackPanel>
    </DataTemplate>

    <Style x:Key="TabItemStyle" TargetType="TabItem">
        <Style.Triggers>
            <!-- Template Application Approach-->
            <DataTrigger Binding="{Binding Path=Type}" Value="{x:Type local:AViewModel}">
                <Setter Property="HeaderTemplate" Value="{StaticResource ATypeHeader}" />
                <Setter Property="ContentTemplate" Value="{StaticResource ATypeContent}" />
            </DataTrigger>

            <!-- Just Use Setters Approach -->
            <DataTrigger Binding="{Binding Path=Type}" Value="{x:Type local:BViewModel}">
                <Setter Property="Header">
                    <Setter.Value>
                        <WrapPanel>
                            <TextBlock Text="B Header!"></TextBlock>
                            <TextBlock Text="{Binding Header}" />
                        </WrapPanel>
                    </Setter.Value>
                </Setter>
                <Setter Property="Content" Value="{Binding Content}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<Grid>
    <TabControl ItemContainerStyle="{StaticResource TabItemStyle}" ItemsSource="{StaticResource MyCollection}" />
</Grid>

HTH,安德森



Answer 4:

在这个例子中,我用我的资源部分的DataTemplates TabControl ,因为我想在标签项目上显示每个视图模型。 在这种情况下,我映射ViewModelType1View1ViewModelType2View2 。 视图模型将被设置为DataContext自动的看法对象。

为了显示标签项头,我使用ItemTemplate 。 视图模型我绑定到不同类型的,而是从一个共同的基类派生ChildViewModel ,有一个Title属性。 所以,我可以建立一个绑定拿起标题标签项目标题来显示它。

此外,我在显示选项卡项目标头中的“关闭”按钮。 如果你不需要的是,只是删除从示例代码的按钮,所以你只要在标题文本。

的标签的项目的内容物被用一个简单ItemTemplate ,其与内容显示视图在内容控制=“{结合}”。

<UserControl ...>
    <UserControl.DataContext>
        <ContainerViewModel></ContainerViewModel>
    </UserControl.DataContext>      
        <TabControl ItemsSource="{Binding ViewModels}"
                    SelectedItem="{Binding SelectedViewModel}">
            <TabControl.Resources>
                <DataTemplate DataType="{x:Type ViewModelType1}">
                    <View1/>
                </DataTemplate>
                <DataTemplate DataType="{x:Type ViewModelType2}">
                    <View2/>
                </DataTemplate>             
            </TabControl.Resources>
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <DockPanel>
                        <TextBlock Text="{Binding Title}" />
                        <Button DockPanel.Dock="Right" Margin="5,0,0,0"
                                Visibility="{Binding RemoveButtonVisibility}" 
                                Command="{Binding DataContext.CloseItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TypeOfContainingView}}}"
                                >
                            <Image Source="/Common/Images/ActiveClose.gif"></Image>
                        </Button>
                    </DockPanel>
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding}"/>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
</UserControl>      


其中包含的标签控制用户控制具有类型的容器视图模型ContainerViewModel作为DataContext 。 在这里,我必须在标签控件中显示的所有视图模型的集合。 我也有当前所选视图模型(标签项目)的属性。

这是我的容器视图模型的缩短版(我跳过了变更通知的部分)。

public class ContainerViewModel
{
    /// <summary>
    /// The child view models.
    /// </summary>
    public ObservableCollection<ChildViewModel> ViewModels {get; set;}

    /// <summary>
    /// The currently selected child view model.
    /// </summary>
    public ChildViewModel SelectedViewModel {get; set;}
}


Answer 5:

约什-史密斯正是使用这一技术在他的优秀文章和示例项目(驱动与视图模型集合的标签控制的) WPF应用程序与模型-视图-视图模型设计模式 。 在这种方法中,因为虚拟机集合中的每个项目都有一个相应的DataTemplate链接查看到VM类型(通过省略X:关键安德森艾姆斯正确地指出),每个标签可以有一个完全不同的UI。 详情请参阅完整的文章和源代码。

XAML中的关键部分是:

 <DataTemplate DataType="{x:Type vm:CustomerViewModel}">
   <vw:CustomerView />
 </DataTemplate>

<DataTemplate x:Key="WorkspacesTemplate">
<TabControl 
  IsSynchronizedWithCurrentItem="True" 
  ItemsSource="{Binding}" 
  ItemTemplate="{StaticResource ClosableTabItemTemplate}"
  Margin="4"
  />

有一个缺点 - 从驾驶WPF TabControl的一个的ItemsSource有性能问题,如果标签中的UI大/复杂,因此缓慢的绘制(例如,大量数据的数据网格)。 有关这方面的问题,所以搜索“WPF VirtualizingStackPanel以提高性能”。



文章来源: WPF TabControl and DataTemplates