0

I want to dynamically add and remove tabs from a wpf TabControl only via databinding. Here is the example XAML:

<Window x:Name="UI_MainWindow" x:Class="DynamicTabs.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DynamicTabs"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid DataContext="{Binding ElementName=UI_MainWindow, Mode=OneWay}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <Button Content="Add Tab" Click="Click_AddTab"/>
        </StackPanel>
        <TabControl x:Name="UI_TabControl" Grid.Row="1" ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}"/>
    </Grid>
</Window>

And here the code behind:

using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace DynamicTabs
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        void OnPropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(name));
        }
        public List<TabItem> Tabs { get; set; }
        public TabItem SelectedTab { get; set; }
        public MainWindow()
        {
            InitializeComponent();
            Tabs=new List<TabItem>();
        }
        private void Click_AddTab(object sender,RoutedEventArgs e)
        {
            TabItem newTab=new TabItem();
            newTab.Header="Tab"+(Tabs.Count+1).ToString();
            Tabs.Add(newTab);
            OnPropertyChanged(nameof(Tabs));
            SelectedTab=newTab;
            OnPropertyChanged(nameof(SelectedTab));
        }
    }
}

The behaviour is like this: First click on "Add Tab": one tab is shown with header "Tab1" Any further clicks on "Add Tab": the items of the TabControl are increased, also the SelectedItem seems to be updated, but no new headers are drawn.

I tried a workaround (see https://www.codeproject.com/Articles/493538/Add-Remove-Tabs-Dynamically-in-WPF) by setting the DataContext of the TabControl programmatically, which works. However, i would like to understand, why my version does not work.

FST
  • 1
  • Well, you certainly can't update `SelectedTab` like you're doing without raising `PropertyChanged`. Did you just omit that for brevity? And `Tabs` needs to be `ObservableCollection`, not `List`. Otherwise how is the `TabControl` supposed to know anything changed? – 15ee8f99-57ff-4f92-890c-b56153 May 17 '17 at 18:38
  • You should not be implementing `INotifyPropertyChanged` on a window, and it's usually a very bad idea to have a List or ObservableCollection of a control type like `TabItem`, but `TabControl` is kind of a special case with that rule, because *mumble mumble virtualization*. – 15ee8f99-57ff-4f92-890c-b56153 May 17 '17 at 18:41

1 Answers1

1

However, i would like to understand, why my version does not work.

Because you are binding to a List<TabItem> instead of an ObservableCollection<TabItem>. If you simply change the type of the source collection it works:

public ObservableCollection<TabItem> Tabs { get; set; }
public TabItem SelectedTab { get; set; }
public MainWindow()
{
    InitializeComponent();
    Tabs = new ObservableCollection<TabItem>();
}

The difference between the two collection types is that the latter implements the INotifyCollectionChanged interface. Implementing this interface is required if you want the ItemsControl in the view to get updated automatically when you add or remove an item to or from the source collection.

mm8
  • 163,881
  • 10
  • 57
  • 88