我有一个带有单个特定选项卡的 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
并通知它没有使用任何 ItemTemplate
或 ItemTemplateSelector
用于创建它。您可以简单地忽略此错误,因为最终控件应该看起来像您想要的那样(我想)。
另一种(可能更简洁)的方法不是在一个集合中混合 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/