以下只创建了一个MyTabView
实例。我通过在MyTabView.xaml.cs中的构造函数中放置一个断点来证实这一点。视图显示在一个选项卡中,无论我创建了多少个选项卡,我只打过一次构造函数。
<DataTemplate DataType="{x:Type vm:MyTabViewModel}" x:Shared="false">
<vw:MyTabView />
</DataTemplate>
标签控件:
<TabControl
Grid.Row="1"
ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTab}"
>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl>
这会导致所有选项卡反映共享任何未绑定到视图模型的状态:如果移动GridSplitter
,则在所有其他选项卡中都是相同的GridSplitter
,因此用户看起来已移动所有这些选项卡。这太荒谬了。
我不明白。有没有办法使用TabControl
与同一类型的多个项目?
编辑:添加x:Shared="false"
到DataTemplate
。
更新:
所以我发现了一些修复,但我不太喜欢它们。我打算看看编写一个将ObservableCollection<Object>
转换为ObservableCollection<TabItem>
的转换器 - 有点像实时更新版本
coll.Select(vm => new TabItem() { Content = vm });
...但我们会看看是否喜欢从TabItem
获取ItemsSource
实例。我的钱说不要赌它。但我们会看到。
更新2:花了一段时间才回到这一点。交换标签项集合的噱头有效,但SelectedItem
有问题。结果是另一个解决方案(下面)没有创建该问题,并且还避免了创建必须镜像源集合中的更改的“中间人”集合的复杂性和愚蠢性。
更新:非常确定这里真正的问题是通常的TabControl
虚拟化bu ^ H ^ Hfeature。
解决方案是在加载每个选项卡时跳转并显式实例化模板。
XAML
<TabControl
Grid.Row="1"
ItemsSource="{Binding Tabs}"
>
<TabControl.Resources>
<Style TargetType="TabItem">
<EventSetter Event="Loaded" Handler="TabItem_Loaded" />
</Style>
</TabControl.Resources>
</TabControl>
C#
private void TabItem_Loaded(object sender, RoutedEventArgs e)
{
var tabItem = (sender as TabItem);
if (null != tabItem.DataContext)
{
// Hey, what about TabControl.ItemTemplate, eh?
var dataTemplateKey = new System.Windows.DataTemplateKey(tabItem.DataContext.GetType());
var dataTemplate = (DataTemplate)tabItem.FindResource(dataTemplateKey);
tabItem.Content = dataTemplate.LoadContent();
(tabItem.Content as FrameworkElement).DataContext = tabItem.DataContext;
}
}
...但是当然有更好的错误处理。
你无法通过DataContextChanged
处理EventSetter
,因为它不是一个路由事件。你得到这个:
System.Windows.FrameworkElement.DataContextChanged =“TabItem_DataContextChanged”无效。 'DataContextChanged'必须是注册的RoutedEvent,其名称以关键字“Event”结尾。
但是Loaded
在这种情况下做得很好。
尝试在模板中添加x:Shared="false"
属性。
当使用ElementName
接受的方法时,我在DataTemplate
中遇到dataTemplate.LoadContent
绑定问题。
在@EdPlunkett的解决方案的基础上,我尝试使用每个ContentPresenter
的中间TabItem
,它适用于我的使用场景。
TabControl
虚拟化是否适用于我的标签数量(我没有检查在我的情况下虚拟化会发生什么)XAML:
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<EventSetter Event="Loaded" Handler="TabItem_Loaded"/>
</Style>
</TabControl.ItemContainerStyle>
<TabControl.Resources>
<DataTemplate x:Key="MyTemplateKey">
...
</DataTemplate>
</TabControl.Resources>
代码背后(我将DataTemplateKey
更改为基于字符串的键,仅用于具有命名模板资源的用例。用于选择模板的DataTemplateKey
方法也应该起作用)
private void TabItem_Loaded(object sender, RoutedEventArgs e)
{
var tabItem = (sender as TabItem);
if (null != tabItem.DataContext)
{
var dataTemplate = (DataTemplate)tabItem.FindResource("MyTemplateKey");
var cp = new ContentPresenter();
cp.ContentTemplate = dataTemplate;
cp.Content = tabItem.DataContext;
tabItem.Content = cp;
}
}