-1

The existing question had an answer, but it seems to recreate the control when the tab index changes. Can I prevent this and keep the controls?

enter image description here

<TabControl Name="MyTabControl" IsSynchronizedWithCurrentItem="True" >
    <TabControl.Resources>
        <DataTemplate x:Shared="False" x:Key="MyTemplate">
            <TextBox></TextBox>
        </DataTemplate>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="ContentTemplate" Value="{StaticResource MyTemplate}"/>
        </Style>
    </TabControl.Resources>        
   
</TabControl>

code behind

public partial class MainWindow : Window
{
    ObservableCollection<string> Tabs = new ObservableCollection<string>();
    public MainWindow()
    {
        InitializeComponent();
        Tabs = new ObservableCollection<string>();
        Tabs.Add("Tab1");
        Tabs.Add("Tab2");

        MyTabControl.ItemsSource = Tabs;
    }       
}

A workaround... seems to work for now.

<TabControl Name="MyTabControl">
    <TabControl.Resources>
        <DataTemplate x:Key="MyTemplate">
            <ContentControl Content="{Binding RealControl}"></ContentControl>
        </DataTemplate>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="ContentTemplate" Value="{StaticResource MyTemplate}"/>
            <Setter Property="Header" Value="{Binding Header}"/>
        </Style>
    </TabControl.Resources>        
</TabControl>

code behind

public partial class MainWindow : Window
{
    ObservableCollection<Thing> Tabs = new ObservableCollection<Thing>();
    public MainWindow()
    {
        InitializeComponent();
        Tabs = new ObservableCollection<Thing>();
        Tabs.Add(new Thing("Tab1"));
        Tabs.Add(new Thing("Tab2"));

        MyTabControl.ItemsSource = Tabs;
    }       
}

class Thing
{
    public Thing(string name)
    {
        Header = name;
    }
    public string Header { get; set; }
    public TextBox RealControl { get; set; } = new TextBox();
}
Damn Vegetables
  • 11,484
  • 13
  • 80
  • 135
  • If these are all supposed to be the same control with same state, what's the point of multiple ones in a tabcontrol? It's one control. – Andy Jul 30 '20 at 19:50

1 Answers1

0

When you use the x:Shared attribute set to False, then each time you switch the tab new controls in the content of your tab item will be created from the data template. That means all state that is not saved in any way, like being bound to a view model, will be lost. Since you do not store the value of the Text property on the TextBox, it will be lost once you switch tabs, as a new TextBox will be created that has default values for its properties.

You can verify this yourself by creating an item type that has a property to store the text value from the TextBox. Then you bind Text to this property and make sure that the property is actually updated by e.g. losing focus after typing in text (LostFocus is the default UpdateSourceTrigger for TextBox). You will see that the value is restored through the binding when switching tabs for each tab individually. That happens because the TextBox controls are newly created, but the bindings will update the Text property from the view model that stored its state.

Can I prevent this and keep the controls?

You have multiple options.

  • If you use x:Shared=False, then you have to save the view state yourself, e.g. through binding to a view model.
  • Do not use the x:Shared attribute then the controls will be reused and only the bound data is updated, since the TabControl will change the DataContext for the tabs.
  • Consider not using a TabControl at all, but only a user control where you change the data context if it is always the same.
thatguy
  • 21,059
  • 6
  • 30
  • 40
  • But the was just for the sake of question. I don't use a simple TextBox for the template. The template will be complicated and saving/restoring all the states of the controls will be very difficult. I mean, even for TextBox, just saving/restoring Text is not enough; I would have to do so for cursor position, scroll position, selection, etc. So, there is no way in WPF just to keep the created control so that I don't have to save/restore the content each time when the index changes? – Damn Vegetables Jul 30 '20 at 19:46
  • If I don't use x:Shared=False, the content is shared by all tabs. And I would have to save/restore the content anyway using binding and something, so basically the same problem. The reason I searched for this in the first place was to avoid that and keep separate controls as they are in each tab. – Damn Vegetables Jul 30 '20 at 19:53
  • @DamnVegetables Ok, now I understand your reasons behind that. However, I think you will have to deal with one or the other. So either you create multiple control instances where you have to keep track of the control states or you synchronize the data with view models, which would be the preferable way in my opinion and clean MVVM. – thatguy Jul 30 '20 at 19:58
  • I was going to keep the control in the bound item itself, if there is no clean way to do that natively. Since there seems no way, I tried it and it seemed to work. I will appended that to the original question. – Damn Vegetables Jul 30 '20 at 20:02