WPF带有DataTemplate的TabControl表现非常奇怪

5
如果您在DataTemplate中放置控件,为什么它们的单独状态会被复制或反映在TabControl的每个选项卡中?您在一个选项卡中进行更改,所有其他选项卡都会反映,为什么会这样?!对我而言,TabControl只初始化一个模板化的ContentControl,每次单击选项卡时都会将整个内容复制一遍 - 保留旧的控件状态不变。要查看我的意思,请在您的XAML-Pad中放置以下代码:
<TabControl>
  <TabControl.ContentTemplate>
    <DataTemplate>
      <Border>
        <TextBox Text="test"/>
      </Border>
    </DataTemplate>
  </TabControl.ContentTemplate>
  <TabItem Header="Tab1"/>
  <TabItem Header="Tab2"/>
</TabControl>

它将创建一个带有两个模板标签的TabControl。现在在TextBox中输入一些内容并切换到另一个选项卡,输入的文本将被保留。每个选项卡现在都将具有相同的内容。我没有观察到ListBox或任何其他控件中的相同行为,这使得实际工作非常困难,因为每一点都需要绑定到ViewModel才能在TabControl中使用。当我在DataTemplate中使用Expanders时,我注意到了这种奇怪的行为,它会在所有选项卡中弹出,尽管我明确地指出了一个。为了解决这个问题,我不得不将“IsExpanded”绑定到ViewModel中的属性,但这真的很糟糕。
有人知道这里发生了什么吗?
解决方案
<TabControl x:Name="MainTab" SelectedIndex="0"/>
...
Collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Collection_CollectionChanged);
...
void Collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        TabItem MyNewItem = new TabItem();
        ContentPresenter MyContentPresenter = new ContentPresenter();
        MyContentPresenter.ContentTemplate = (DataTemplate)this.FindResource("MyTemplate");
        MyContentPresenter.Content = e.NewItems[0];
        MyNewItem.Content = MyContentPresenter;                
        MainTab.Items.Add(MyNewItem );
    }
}

我认为ContentTemplate和ItemTemplate属性应该应用于ItemsSource属性的项目,而不是显式定义的项目。但是内置的TabControl没有ItemsSource属性,因此您可以使用扩展的TabControl:http://vortexwolf.wordpress.com/2011/04/09/silverlight-tabcontrol-with-data-binding/。但是我不知道这个特定示例有什么问题。无论如何,Silverlight TabControl与其WPF对应物不同,所以可能是一个错误。 - vortexwolf
我尝试将模板应用于TabItem本身,这会产生相同的结果。至少在WPF下,TabControl具有我用于填充其内容的ItemsSource属性。在您的扩展版本中,DataTemple内部的控件是否保留其状态?您尝试在其中放置复选框或其他控件并查看是否都被选中了吗?我可能会尝试一下...感谢您的时间和最好的祝福。 - hpalu
2个回答

2
行为是完美的,有一种解决方法。只有在绑定的情况下才应使用DataTemplate。当您将可枚举项源分配给选项卡控件时,您的数据模板应该并且将包含单向或双向绑定。在文本框的情况下,它应该是双向绑定。
TabControl这样做是为了节省内存,在数据模板的情况下,当您切换选项卡时,控件保持不变,但是其底层绑定的数据上下文发生更改,并且绑定反映正确的数据。因此,视觉上感觉选项卡已更改,但实际上只有数据更改,但控件保持不变。这称为UI虚拟化。
在您的情况下,除非将某些内容绑定到项源,否则不应使用数据模板。否则,您必须使用项容器样式。

那么在这种情况下,为什么不创建单独的选项卡项目并将它们作为选项卡控件的子项添加呢?我理解你想做什么,但在这种情况下,您无法保持编辑状态。如果您定义数据模板,那么每次切换时,选项卡项目将被卸载并加载新项目,如果您在加载事件上放置跟踪,则每次切换都会触发该事件。这种行为对于数据绑定的可视化呈现是常见的,但如果您谈论编辑状态,则状态本身就成为数据。 - Akash Kava
如果选项卡控件包含具有UI元素的选项卡项,则它将保留所有内容。 - Akash Kava
问题是,你必须绑定所有内容,而不仅仅是内容本身。在我的程序中,我使用了数据绑定,当然内容不会交换,但其他所有元素都会交换,例如列表框中滚动条的位置、选定的项目、展开器的状态等等,这使得它几乎无法使用,让我的客户感到困惑。 :-( - hpalu
哦,对不起,SO不让我改我的评论,所以我删除了并新建了一个,现在太晚了。我现在会尝试一下,非常感谢你!!(-: - hpalu
它有效了!非常感谢,我很快就会把解决方案放在那里! - hpalu
我通过这个提示 WPF TabControl: Turning Off Tab Virtualization 在 http://www.codeproject.com/Articles/460989/WPF-TabControl-Turning-Off-Tab-Virtualization 解决了它。这是一个具有属性IsCached的TabContent类。 - Alexsandro

1

在WPF中,TabControls的使用略显棘手。首先,当您切换选项卡时,它会销毁第一个选项卡上的所有内容,并加载第二个选项卡的控件。唯一不会被销毁/重新创建的是存在于所有选项卡上的控件,例如在TabItem的模板中创建的TextBox。

因此,您的TextBox将在其他选项卡上重复使用,并且由于TextBox.Text未绑定到任何东西,所以在切换选项卡时保持不变。

要更改此行为,请将TextBox.Text值绑定到某个东西,或者为每个TabItem创建其自己的版本的TextBox。

<TabControl>
    <TabItem Header="Tab1">
        <local:TabControlContent />
    </TabItem>
    <TabItem Header="Tab2">
        <local:TabControlContent />
    </TabItem>
</TabControl>

非常感谢,至少解释清楚了!但正如我之前所写的那样,仅仅绑定内容似乎是不够的,因为每个可见状态也会被传递过去,包括滚动条位置、选定项、展开状态等等。这意味着我必须在我的ViewModel中添加垃圾代码。我认真考虑自己编写TabControl,我不知道他们怎么能发布这样的东西。 :-( - hpalu
@hpalu 那么最简单的方法可能是创建一个 UserControl 作为您的 TabContent,然后将每个 TabItem 的内容设置为 UserControl 的新实例。这样应该会保持每个选项卡的单独实例,尽管非绑定值(如 ListBox 位置和 IsExpanded 值)在切换选项卡时将被重置。为了保持这些值,我通常使用扩展的 TabControl,在此处找到:http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-from-destroying-its-children.aspx - Rachel
哇,非常感谢你,Rachel!如果这能帮我省去写自己版本的麻烦,我会非常感激的。:D - hpalu
Rachel,我使用了Akash的解决方案,它现在为我节省了很多麻烦,但我仍在阅读你发送给我的材料,再次感谢你! - hpalu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接