DataTemplate 和 ItemTemplate 的数据错误 26

Data Error 26 with DataTemplate and ItemTemplate

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

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

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

System.Windows.Data Error: 26 : ItemTemplate and ItemTemplateSelector are ignored for items already of the ItemsControl's container type; Type='TabItem'

看起来 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>

您观察到的错误非常明显。
您将 TabControlItemsSource 定义为包含不同类型元素的 CompositeCollection

  • 一个TabItem"General";
  • 一堆 Person 视图模型。

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

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

另一种(可能更简洁)的方法是不要将视图和视图模型混合在一个 collection 中,而是为 "General" 选项卡指定一个 "general" 视图模型:

<TabControl.ItemsSource>
    <CompositeCollection>
        <viewModel:GeneralViewModel/>
        <CollectionContainer Collection="{Binding Source={StaticResource personCollection}}" />
    </CompositeCollection>
</TabControl.ItemsSource>

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

<TabControl.Resources>
    <DataTemplate DataType="{x:Type viewModel:GeneralViewModel}">
        <local:GeneralView />
    </DataTemplate>
    <!-- ... -->
</TabControl.Resources>

更新

解决您评论中的问题。

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

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

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return Proxy.DataContext;
    }   
}

在您的 collection:

中将此标记扩展与绑定代理一起使用
<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>

您可以根据需要扩展标记扩展,例如以这样一种方式,它可以观察 object 更新并替换目标 CompositeCollection.

中的项目

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

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

然后你可以为不同的TabItem定义不同的ItemTemplate

<TabControl.Resources>
    <!-- ... -->
    <DataTemplate x:Key="GeneralTabItemTemplate">
        <TextBlock Text="General" />
    </DataTemplate>
    <DataTemplate x:Key="PersonTabItemTemplate">
        <TextBlock Text="{Binding FirstName}" />
    </DataTemplate>
</TabControl.Resources>

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