wpf - DataTemplate 和 ItemTemplate 的数据错误 26

标签 wpf xaml datatemplate

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

我还设置了一个 ItemTemplate 来定义带有绑定(bind)的选项卡项的名称,但它没有在资源中定义,因为我猜它会与“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>

最佳答案

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

  • 一个 TabItem “一般的”;
  • 一堆Person View 模型。

  • 所以你只是在一个集合中混合了一个 View 和一些 View 模型——这并不整洁。 WPF 通过错误消息通知您这一点。引擎尝试为项目创建 View (使用 DataTemplate s)并突然遇到一个已经指定的 View (一个 TabItem ),它正是项目容器的类型(因为对于 TabControl ,每个 View 都有一个viewmodel 将被插入到 TabItem 容器中)。所以 WPF 只需插入 TabItem进入TabControl并通知它没有使用任何 ItemTemplateItemTemplateSelector用于创建它。

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

    另一种(可能更简洁)的方法不是在一个集合中混合 View 和 View 模型,而是为“常规”选项卡指定一个“通用” View 模型:
    <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 绑定(bind)到 DataContext 中存在的那个?
    这是可能的,但有一些开销。您必须为此创建一个绑定(bind)代理。 (看看 here 。)
    您需要的第二件事是标记扩展:
    class BindingProxyValue : MarkupExtension
    {
        public BindingProxy Proxy { get; set; }
    
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return Proxy.DataContext;
        }   
    }
    

    将此标记扩展与集合中的绑定(bind)代理一起使用:
    <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>
    

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

    2.
    如何指定常规选项卡的标题名称?
    您可以使用 ItemTemplate s,但它变得有点复杂。您必须实现 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;
        }
    }
    

    然后你可以定义不同的ItemTemplate s 代表不同 TabItem年代:
    <TabControl.Resources>
        <!-- ... -->
        <DataTemplate x:Key="GeneralTabItemTemplate">
            <TextBlock Text="General" />
        </DataTemplate>
        <DataTemplate x:Key="PersonTabItemTemplate">
            <TextBlock Text="{Binding FirstName}" />
        </DataTemplate>
    </TabControl.Resources>
    

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

    关于wpf - DataTemplate 和 ItemTemplate 的数据错误 26,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44457888/

    相关文章:

    wpf - WPF-显示带有数据模板的单个实体

    c# - WPF 方形单元

    c# - WPF ItemsControl水平方向并填充父级?

    xaml - 如何创建可用于所有 xamarin 表单页面的页脚?

    c# - 在数据模板中使用抽象类作为数据类型

    wpf - 将 DataTemplate 应用到网格

    wpf - 复选框失去焦点与 FocusManager.IsFocusScope ="True"

    wpf - 实现 WPF 捕捉网格

    WPF DataGrid 将字符串绑定(bind)列表作为行

    c# - 如何传播 WPF 数据绑定(bind)期间发生的错误和异常?