How to bind tabs
When your application needs to display a variable number of tabs, you can data-bind a TabControl to a collection of view models instead of declaring each tab statically in XAML. This approach is useful when the number of tabs is determined at runtime, for example by user actions, loaded data, or plugin systems.
The general pattern is:
- Define a view model class that represents each tab (header text, content, and any other state).
- Expose an
ObservableCollectionof those view models from your main view model. - Bind
TabControl.ItemsSourceto the collection and useItemTemplateandContentTemplateto control how each tab renders.
Binding support example
You can dynamically create tab items with data binding. To do this, bind the ItemsSource property of a TabControl to a collection of objects representing the tab header and content.
You can then use a data template to display the objects.
This example uses a collection of objects created from this ItemViewModel class:
namespace MyApp.ViewModel;
public class ItemViewModel
{
public string Header { get; }
public string Content { get; }
public ItemViewModel(string header, string content)
{
Header = header;
Content = content;
}
}
Create a property that accesses a collection of ItemViewModel instances.
public ObservableCollection<ItemViewModel> Items { get; set; } = new() {
new ItemViewModel("One", "Some content on first tab"),
new ItemViewModel("Two", "Some content on second tab"),
};
The TabStrip header content is defined by the ItemTemplate property, while the TabItem content is defined by the ContentTemplate property.
Finally, create a TabControl and bind its ItemsSource property to Items.
<TabControl ItemsSource="{Binding Items}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- ContentTemplate's DataTemplate must specify the view model in DataType.
The alias 'vm' references the specification of the view model's namespace in
an attribute of the XAML's root element, which will look like
xmlns:vm="using:MyApp.ViewModel"
or
xmlns:vm="clr-namespace:MyApp.ViewModel;assembly=MyApp.ViewModel" -->
<DataTemplate DataType="vm:ItemViewModel">
<DockPanel LastChildFill="True">
<TextBlock Text="This is content of selected tab" DockPanel.Dock="Top" FontWeight="Bold" />
<TextBlock Text="{Binding Content}" />
</DockPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Tab lifecycle notes
Keep the following points in mind when you work with data-bound tabs:
- Adding and removing tabs. Because
ItemsSourceis bound to anObservableCollection, adding or removing items from the collection automatically adds or removes tabs at runtime. - Content recycling.
TabControlrecreates content visuals when the user switches tabs. If your tab content is expensive to build, consider caching the generated views or using aUserControlwith its own view model to preserve state. - Selected tab. Bind
SelectedItemorSelectedIndexon theTabControlto track or control which tab is active. When you remove the currently selected item from the collection, the selection resets automatically. - DataType on ContentTemplate. Always set
DataTypeon theDataTemplateused insideContentTemplate. Without it, the binding context may not resolve correctly.
See also
- TabControl: Full reference for the
TabControlcontrol. - How to: Work with TabControl: Static tabs, closeable tabs, and tab styling.
- Data templates: Controlling how items are displayed.
- Data binding syntax: Binding paths and modes.