5

If you put controls in your DataTemplate, why are their individual states copied or reflected in each and every Tab in the TabControl? You change it in one Tab, all other Tabs reflect, why is that?! It seems to me the TabControl initializes one templated ContentControl only and each click on a Tab copies the entire content in it anew - leaving old controlstates untouched. To see what i mean consider putting this in your XAML-Pad:

<TabControl>
  <TabControl.ContentTemplate>
    <DataTemplate>
      <Border>
        <TextBox Text="test"/>
      </Border>
    </DataTemplate>
  </TabControl.ContentTemplate>
  <TabItem Header="Tab1"/>
  <TabItem Header="Tab2"/>
</TabControl>

It will create a TabControl with two templated tabs. Now enter something in the TextBox and switch to another Tab, the entered text will carry over. Each Tab will have the same content now. I do not observe the same behavior in a ListBox or any other control and it makes practical work very hard because every little bit needs to be bound to a ViewModel to make it usable in a TabControl. I noticed this weird behavior when Expanders i used in a DataTemplate popped open in all of my tabs, although i specifically addressed one. As a workaround i had to bind "IsExpanded" to a property in the ViewModel, but it really sucks having to do that.

Anyone knows what's happening here?


SOLUTION

<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 );
    }
}
hpalu
  • 1,357
  • 7
  • 12
  • I think the properties ContentTemplate and ItemTemplate should be applied to items of the ItemsSource property, not to explicitly-defined items. But the built-in TabControl doesn't have the ItemsSource property, so you can use the extended TabControl: http://vortexwolf.wordpress.com/2011/04/09/silverlight-tabcontrol-with-data-binding/. But I don't know what's wrong with this particular example. Anyway the silverlight tab control is different from its WPF counterpart, so maybe it's a bug. – vortexwolf Oct 09 '11 at 11:53
  • I tried to apply the template on the TabItem itself, which gives the same result. TabControl under WPF atleast has ItemsSource which i use to populate it. In your extended version the controls inside a DataTemple do not carry over their states? Have you tried putting a checkbox or something in there and see if one check goes for all? I might give it a try ... thanks for your time and best regards – hpalu Oct 09 '11 at 12:43

2 Answers2

2

The behavior is perfect and there is a way to resolve this. DataTemplate must be used only in case of binding. When you assign an enumerable items source to tab control, your data template should and will consist either one or two way bindings. In case of text box, it should be two way binding.

TabControl does this to save memory, in case of data template, control remains same when you switch tab, but it's the under lying bound datacontext is what changes, and binding reflects correct data. So visually you feel as tab has changed, but actually only data changes, however control remains same. This is called ui virtualization.

In your case you should not use data template unless you bind something to items source. Otherwise you must use item container style.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • Well then in that case, why don't you create individual tab items and add them as children of tab control? I understand what you are trying to do, but in that case you can not persist editing state. If you define data template then everytime you will switch, tab item will be unloaded and new item will be loaded and if you put trace on load event it will be fired everytime you switch. This behavioir is common for data bound visual presentation but if you are talking about editing state then state itself becomes data. – Akash Kava Oct 10 '11 at 19:03
  • If tab control contains tab item with ui element it will preserve everything. – Akash Kava Oct 10 '11 at 19:04
  • The problem is, you have to bind everything, not just the content. In my program i do use databindings and of course the content does not swap over, but everything else does. For instance the position of a scrollbar in a listbox, the selected Item, the state of an expander, etc., etc., etc. It makes it practically unusable and confuses my clients. :-( – hpalu Oct 10 '11 at 19:04
  • Oh im sorry, SO wouldnt let me change my comment so i deleted and made a new one, too late now. I will try it out now, thank you so, so much!! (-: – hpalu Oct 10 '11 at 19:06
  • It works! thanks alot, i'll put the solution up there in no time! – hpalu Oct 10 '11 at 19:25
  • I got resolve it with this tip `WPF TabControl: Turning Off Tab Virtualization` at http://www.codeproject.com/Articles/460989/WPF-TabControl-Turning-Off-Tab-Virtualization this a class of TabContent with property IsCached. – Alexsandro Mar 24 '16 at 20:09
1

TabControls in WPF are slightly obnoxious to work with. For one, when you switch tabs it destroys everything on the first tab, and loads up the controls for the 2nd tab. The only thing that doesn't get destoryed/recreated is the controls that exist on all tabs, such as a TextBox that is created in the TabItem's Template.

So your TextBox is getting re-used on the other tabs, and since the TextBox.Text is not bound to anything, it stays the same when switching tabs.

To change this behavior, either bind the TextBox.Text value to something, or create each TabItem with it's own version of the TextBox.

<TabControl>
    <TabItem Header="Tab1">
        <local:TabControlContent />
    </TabItem>
    <TabItem Header="Tab2">
        <local:TabControlContent />
    </TabItem>
</TabControl>
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Thanks so much, that at least explains it! But as i wrote before, simply binding the content seems to be not enough, since each visible state is carried over aswell, scrollbar positions, selected items, expanded-states, etc. That would mean i'd have to bloat my ViewModel with junk. I seriously consider writing the TabControl myself, i have no idea how they could release it like this. :-( – hpalu Oct 10 '11 at 18:46
  • @hpalu The easiest way then would probably be to create a `UserControl` of your TabContent, and then set the content of each TabItem to a new instance of the UserControl. That should keep separate instances of each tab, although the non-bound values such as ListBox positions and IsExpanded values will get reset when you switch tabs. To maintain those values, I usually use the extended TabControl found here: http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-from-destroying-its-children.aspx – Rachel Oct 10 '11 at 18:49
  • Wow, thanks so much Rachel! If that helps to save me the trouble of writing my own version i would really appreciate it. :D – hpalu Oct 10 '11 at 19:05
  • Rachel, i used Akashs solution, it saves me alot of trouble right now, but i'm still reading through the material you have sent me, thanks alot again! – hpalu Oct 10 '11 at 19:31