XAML DataTemplate只创建一个实例

问题描述 投票:3回答:3

以下只创建了一个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有问题。结果是另一个解决方案(下面)没有创建该问题,并且还避免了创建必须镜像源集合中的更改的“中间人”集合的复杂性和愚蠢性。

wpf xaml datatemplate tabcontrol
3个回答
1
投票

更新:非常确定这里真正的问题是通常的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在这种情况下做得很好。


0
投票

尝试在模板中添加x:Shared="false"属性。


0
投票

当使用ElementName接受的方法时,我在DataTemplate中遇到dataTemplate.LoadContent绑定问题。

在@EdPlunkett的解决方案的基础上,我尝试使用每个ContentPresenter的中间TabItem,它适用于我的使用场景。

  • 具有相同模板和相同datacontext的多个选项卡键入但包含不同的实际数据
  • DataTemplate中的GridSplitter
  • ElementName在DataTemplate中绑定
  • 我不关心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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.