0

I have the following TabControl:

<TabControl ItemsSource="{Binding Tabs"}>
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type vm:TabVM}">
            <TextBox></TextBox>
            <TextBox Text="{Binding SomeProperty}"></TextBox>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

The unexpected behaviour is that first TextBox has Text property shared between all tabitems, while second TextBox effectively bind to ViewModel property.

My need is to make independent the first TextBox too, even without binding.

What can I do ?

** UPDATE **

After several tries I've decided to use the ikriv's TabContent.cs. The only issue I've found with this is that calling the TabControl.Items.Refresh() (i.e. after removing a tabItem) cause the reset of the internal cache.

An unelegant but effective solution may be this:

public ContentManager(TabControl tabControl, Decorator border)
{
    _tabControl = tabControl;
    _border = border;
    _tabControl.SelectionChanged += (sender, args) => { UpdateSelectedTab(); };

    /* CUSTOM */
    var view = CollectionViewSource.GetDefaultView(((TabControl)_tabControl).Items);
    view.CollectionChanged += View_CollectionChanged;
}

/*
 * This fix the internal cache content when calling items->Refresh() method
 * */
private void View_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if (e.OldItems != null)
    {
        /* Retrieve all tabitems cache and store to a temp list */
        IList<ContentControl> cachedContents = new List<ContentControl>();

        foreach (var item in _tabControl.Items)
        {
            var tabItem = _tabControl.ItemContainerGenerator.ContainerFromItem(item);

            var cachedContent = TabContent.GetInternalCachedContent(tabItem);

            cachedContents.Add(cachedContent);
        }

        /* rebuild the view */
        _tabControl.Items.Refresh();

        /* Retrieve all cached content and store to the tabitems */
        int idx = 0;

        foreach (var item in _tabControl.Items)
        {
            var tabItem = _tabControl.ItemContainerGenerator.ContainerFromItem(item);

            TabContent.SetInternalCachedContent(tabItem, cachedContents[idx++]);
        }
    }
}
Fabrizio Stellato
  • 1,727
  • 21
  • 52

1 Answers1

0

You should use data binding since the same ContentTemplate will be applied for all items in your ItemsSource. Only the binding will be refreshed when you switch tabs basically. The TextBox isn't re-created nor reset.

What can I do ?

You could work around this in the view by handling the SelectionChanged event of the TabControl and reset the TextBox control yourself:

private void tabs_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        TabControl tc = sender as TabControl;
        ContentPresenter cp = tc.Template.FindName("PART_SelectedContentHost", tc) as ContentPresenter;
        if(cp != null && VisualTreeHelper.GetChildrenCount(cp) > 0)
        {
            ContentPresenter cpp = VisualTreeHelper.GetChild(cp, 0) as ContentPresenter;
            if(cpp != null)
            {
                TextBox textBox = cpp.FindName("txt") as TextBox;
                if (textBox != null)
                    textBox.Text = string.Empty;
            }
        }
    }

    <TabControl x:Name="tabs" ItemsSource="{Binding Tabs}" SelectionChanged="tabs_SelectionChanged">
        <TabControl.ContentTemplate>
            <DataTemplate>
                <ContentPresenter>
                    <ContentPresenter.Content>
                        <StackPanel>
                            <TextBox x:Name="txt"></TextBox>
                        </StackPanel>
                    </ContentPresenter.Content>
                </ContentPresenter>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>

If you want to persist the text in the TextBox when you switch tabs you could use the attached behaviour from the following article and set its IsCached property to true: https://www.codeproject.com/articles/460989/wpf-tabcontrol-turning-off-tab-virtualization

<TabControl ItemsSource="{Binding Items}" behaviors:TabContent.IsCached="True">
    <!-- Make sure that you don't set the TabControl's ContentTemplate property but the custom one here-->
    <behaviors:TabContent.Template>
        <DataTemplate>
            <StackPanel>
                <TextBox />
            </StackPanel>
        </DataTemplate>
    </behaviors:TabContent.Template>
</TabControl>

Yet another approach would be to modify the ControlTemplate of the TabControl to include a ListBox as suggested by 'gekka' in the following thread on the MSDN forums: https://social.msdn.microsoft.com/Forums/en-US/4b71a43a-26f5-4fef-8dc5-55409262298e/using-uielements-on-datatemplate?forum=wpf

mm8
  • 163,881
  • 10
  • 57
  • 88
  • The gekka approach is the best I found, the only issue is that everytime a tab is removed all tabitem's content are reset too – Fabrizio Stellato Dec 19 '16 at 13:54
  • 1
    Please see my edited answer. You should be able to use the solution from the following article: https://www.codeproject.com/articles/460989/wpf-tabcontrol-turning-off-tab-virtualization. – mm8 Dec 21 '16 at 09:35
  • I look at this solution too, but there's an issue when I remove a tab, because all the others one reset its not-binding content – Fabrizio Stellato Dec 21 '16 at 10:49
  • I don't understand what you mean. Could you please provide a repo? – mm8 Dec 21 '16 at 10:55
  • Why are you calling the TabControl.Items.Refresh method at all? The "Tabs" source Collection, i.e. the property that the ItemsSource property of the TabControl is bound to, should be an ObservableCollection that you can dynamically add or remove items from. Then you don't need to reset the TabControl itself. Doing this is not MVVM. – mm8 Dec 29 '16 at 10:16
  • Sometimes you need to do it, because the items need to be refreshed. Think about showing the item's index of a list, for example. http://stackoverflow.com/questions/660528/how-to-display-row-numbers-in-a-listview The only way of displaying the correct item index after a move / delete operation is to call Refresh() method on Items indeed. This can be absolutely done in MVVM, just by placing the correct code on the view – Fabrizio Stellato Dec 29 '16 at 10:56
  • That was fault of mine. For some odd reasons, I realized I added DataContext={Binding } within TextBox element. I removed that line and now works – Fabrizio Stellato Jan 03 '17 at 15:15