代码之家  ›  专栏  ›  技术社区  ›  dustyburwell

WPF选项卡控件和数据模板

  •  8
  • dustyburwell  · 技术社区  · 16 年前

    我想要的是这样的:

    在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 ]"/>
    

    现在,我知道,实际上,每次我用相同的密钥定义数据模板时,系统都会抱怨。但是,有没有类似的方法可以让我基于名称和数据类型将数据模板放入TabControl中?

    5 回复  |  直到 16 年前
        1
  •  18
  •   LiamV    14 年前

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

    <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>
    
        2
  •  8
  •   Anderson Imes    16 年前

    您可以删除x:Key:)这将在遇到给定类型时自动应用模板(可能是WPF、imo最强大且未充分使用的功能之一)。

    这篇WPF博士的文章对数据模板进行了很好的介绍。您需要注意的部分是“ 为给定CLR数据类型定义默认模板

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

    如果这对您的情况没有帮助,您可能可以使用样式(ItemContainerStyle)并使用数据触发器根据类型设置内容和标题,从而完成一些与您所需内容相近的工作。

    下面的示例取决于您的ViewModel,该ViewModel具有一个名为“Type”的属性,该属性的定义与此类似(如果您有基本ViewModel,则很容易放入其中):

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

    所以,只要你有,这应该允许你做任何你想做的事情。注意,我在这里的文本块中有一个“标题!”,但它很容易是任何东西(图标等)。

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

    嗯,安德森

        3
  •  7
  •   Tim Cooper    14 年前

    一种方法是使用 DataTemplateSelector ,并让每个人从单独的 ResourceDictionary .

        4
  •  3
  •   Martin    11 年前

    在本例中,我使用了我的 TabControl 在这种情况下,我需要地图 ViewModelType1 View1 ViewModelType2 View2 视图模型将设置为 DataContext 视图的对象。

    为了显示选项卡项标题,我使用 ItemTemplate . ChildViewModel Title 财产。因此,我可以设置一个绑定以拾取标题,并将其显示在选项卡项标题中。

    此外,我在选项卡项标题中显示“关闭”按钮。如果不需要,只需从示例代码中删除该按钮,就可以得到标题文本。

    选项卡项的内容以简单的方式呈现 普通项 在内容控件中显示视图的 Content=“{Binding}”。

    <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 作为 数据上下文

    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;}
    }
    
        5
  •  1
  •   CyberMonk    16 年前

    Josh Smith在他的优秀文章和示例项目中使用了这种技术(通过视图模型集合驱动选项卡控件) WPF Apps With The Model-View-ViewModel Design Pattern . 在这种方法中,由于VM集合中的每个项都有一个相应的DataTemplate,将视图链接到VM类型(正如Anderson Imes正确指出的那样,通过省略x:Key),因此每个选项卡都可以有一个完全不同的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"
      />