DataTemplate 和 ItemTemplate 的数据错误 26

Cha*_*esB 5 wpf xaml datatemplate

我有一个带有单个特定选项卡的 TabControl 和一个绑定到一组 VM 的集合,使用不同的用户控件。为此,我使用在控件资源中定义的 CompositeCollection 和 DataTemplates,根据 VM 类型(充当 ContentTemplate)选择正确的用户控件。

我还设置了一个 ItemTemplate 来定义带有绑定的选项卡项的名称,但它没有在资源中定义,因为我猜它会与“ContentTemplate”冲突。

它工作正常,但我看到跟踪到以下错误:

System.Windows.Data 错误:26:ItemTemplate 和 ItemTemplateSelector 对于已经属于 ItemsControl 的容器类型的项目被忽略;类型='标签项'

看起来 ContentTemplate 和 ItemTemplate 之间存在一些冲突,但我不知道如何解决?

代码如下:

<TabControl HorizontalAlignment="Left" Height="300" Width="500">
    <TabControl.Resources>
        <CollectionViewSource x:Key="personCollection" Source="{Binding Persons}" />
        <DataTemplate DataType="{x:Type viewModel:Main}">
            <local:MainView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type viewModel:Person}">
            <local:PersonView />
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemsSource>
        <CompositeCollection>
            <TabItem Header="General" Content="{Binding }"/>
            <CollectionContainer Collection="{Binding Source={StaticResource personCollection}}" />
        </CompositeCollection>
    </TabControl.ItemsSource>
    <TabControl.ItemTemplate>
        <DataTemplate DataType="viewModel:Person">
            <TextBlock Text="{Binding FirstName}" />
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>
Run Code Online (Sandbox Code Playgroud)

dym*_*oid 6

您观察到的错误非常明显。
您可以定义ItemsSource您的TabControl作为一个CompositeCollection包含不同类型的元素:

  • 一个TabItem“将军”;
  • 一堆Person视图模型。

所以你只是将一个视图和一些视图模型混合在一个集合中 - 这并不整洁。WPF 通过错误消息通知您。引擎尝试为项目创建视图(使用DataTemplates),突然遇到一个已经指定的视图 (a TabItem),它与项目容器的类型完全相同(因为对于TabControl,每个视图模型的视图将插入到TabItem容器中)。因此,WPF 只是将 插入TabItem到 中TabControl并通知它尚未使用任何ItemTemplateItemTemplateSelector用于创建它。

你可以简单地忽略这个错误,因为最终控件应该看起来像你想要的(我想)。

另一种(可能更简洁)的方法不是在一个集合中混合视图和视图模型,而是为“常规”选项卡指定一个“常规”视图模型:

<TabControl.ItemsSource>
    <CompositeCollection>
        <viewModel:GeneralViewModel/>
        <CollectionContainer Collection="{Binding Source={StaticResource personCollection}}" />
    </CompositeCollection>
</TabControl.ItemsSource>
Run Code Online (Sandbox Code Playgroud)

当然,您还需要告诉 WPF 如何将其可视化:

<TabControl.Resources>
    <DataTemplate DataType="{x:Type viewModel:GeneralViewModel}">
        <local:GeneralView />
    </DataTemplate>
    <!-- ... -->
</TabControl.Resources>
Run Code Online (Sandbox Code Playgroud)

更新

解决您评论中的问题。

1. 如何将 GeneralViewModel 绑定到存在于我的 DataContext 中的那个?
这是可能的,但需要一些开销。您必须为此创建一个绑定代理。(看看这里。)
您需要的第二件事是标记扩展:

class BindingProxyValue : MarkupExtension
{
    public BindingProxy Proxy { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return Proxy.DataContext;
    }   
}
Run Code Online (Sandbox Code Playgroud)

将此标记扩展与集合中的绑定代理一起使用:

<TabControl.Resources>
    <local:BindingProxy x:Key="Proxy" DataContext="{Binding GeneralViewModel}"/>
</TabControl.Resources>
<!--...-->
<CompositeCollection>
   <local:BindingProxyValue Proxy="{StaticResource Proxy}"/>
   <CollectionContainer Collection="{Binding Source={StaticResource personCollection}}" />
</CompositeCollection>
Run Code Online (Sandbox Code Playgroud)

您可以根据需要扩展标记扩展,例如,它可以观察对象更新并替换目标中的项目CompositeCollection

2. 如何指定常规选项卡的标题名称?
您可以使用ItemTemplates,但它会变得有点复杂。你必须DataTemplateSelector为你的TabControl

class YourTabItemDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement element = container as FrameworkElement;

        if (element != null && item != null)
        {
            if (item is GeneralViewmodel)
            {
                return (DataTemplate)element.FindResource("GeneralTabItemTemplate");
            }
            else
            {
                return (DataTemplate)element.FindResource("PersonTabItemTemplate");
            }
        }

        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以ItemTemplate为不同的TabItems定义不同的s:

<TabControl.Resources>
    <!-- ... -->
    <DataTemplate x:Key="GeneralTabItemTemplate">
        <TextBlock Text="General" />
    </DataTemplate>
    <DataTemplate x:Key="PersonTabItemTemplate">
        <TextBlock Text="{Binding FirstName}" />
    </DataTemplate>
</TabControl.Resources>
Run Code Online (Sandbox Code Playgroud)

问题是:这项努力是否值得,或者您对错误消息 26 是否满意?你决定。