11

In the project I am building I have a TabControl in which I want to display a range of tabs through an ItemsSource. I also need to have a few "overview" tabs at the beginning of the TabControl, that cannot be located in ItemsSource.

What is the best way to achieve this, the only way I can think of is to have my overview tabs in my XAML and to just add tab items manually through code instead of using ItemSource is this the best way to go about it.

Hugoagogo
  • 1,598
  • 16
  • 34

4 Answers4

18

You can use CompositeCollection (MSDN) to accomplish this:

<Window.Resources>
   <CollectionViewSource x:Key="ExistingTabs" Source="{Binding ExistingTabs}"/>
</Window.Resources>
<TabControl>
    <TabControl.ItemsSource>
        <CompositeCollection>
             <TabItem>SpecialItem</TabItem>
             <CollectionContainer Collection="{Binding Source={StaticResource ExistingTabs}}"/>
         </CompositeCollection>
    </TabControl.ItemsSource>
</TabControl>
BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • Can you explain the purpose of using `CollectionViewSource` instead just bind directly to `ExistingTabs`? It's because `CompositeCollection` has no `DataContext` inherited or the error _Cannot find governing `FrameworkElement`_ appears otherwise? – Brains Mar 06 '20 at 10:34
  • @Ghosthack I honestly don't remember why, if you try it does it work? – BradleyDotNET Mar 06 '20 at 18:16
  • Yes it works, `CollectionViewSource` was key for it. However, I am just interested why. No problems, I will do more research about `CollectionViewSource`. Thanks for useful answer. – Brains Mar 07 '20 at 11:39
2

For anyone finding the way of using HeaderTemplate / ContentTemplate with CollectionContainer:

First Add Type Property in ViewModel

public Type Type { get { return this.GetType(); } }

Use Style.Triggers to set HeaderTemplate / ContentTemplate for the dynamic tabs identified by the Type property

<Window x:Class="TabDemo.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:TabDemo"
        xmlns:vm="clr-namespace:TabDemo.ViewModel"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        d:DataContext="{d:DesignInstance vm:TabViewModel}">
    <Window.Resources>
        <CollectionViewSource x:Key="ExistingTabs" Source="{Binding ExistingTabs}"/>
        <DataTemplate x:Key="TemplateForTheHeader" DataType="{x:Type vm:TabViewModel}">
            <TextBlock Text="{Binding Title}"/>
        </DataTemplate>
        <DataTemplate x:Key="TemplateForTheContent" DataType="{x:Type vm:TabViewModel}">
            <DockPanel>
                <DataGrid ItemsSource="{Binding Data}"></DataGrid>
            </DockPanel>
        </DataTemplate>
        <Style x:Key="TabItemStyle" TargetType="TabItem">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=Type}" Value="{x:Type vm:TabViewModel}">
                    <Setter Property="HeaderTemplate" Value="{StaticResource TemplateForTheHeader}" />
                    <Setter Property="ContentTemplate" Value="{StaticResource TemplateForTheContent}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <TabControl Grid.Row="1" ItemContainerStyle="{StaticResource TabItemStyle}">
            <TabControl.ItemsSource>
                <CompositeCollection>
                    <TabItem Header="Fixed Header">
                        <TabItem.Content>
                            <TextBlock Text="Fixed Content"/>
                        </TabItem.Content>
                    </TabItem>
                    <CollectionContainer Collection="{Binding Source={StaticResource ExistingTabs}}"/>
                </CompositeCollection>
            </TabControl.ItemsSource>
        </TabControl>
    </Grid>
</Window>

Referencing Anderson Imes's answer: https://stackoverflow.com/a/1348369/1196637

Community
  • 1
  • 1
Sonic.S.Xiang
  • 317
  • 3
  • 5
0

You can use CompositeCollection How do you add a generic item to a ComboBox bound to a collection in WPF

<TabControl>
        <TabControl.ItemsSource>
            <CompositeCollection>
                <TabItem Header="extra tab item"> //Not bound
                    <TextBox>something</TextBox>
                </TabItem>
                <CollectionContainer x:Name="cc"/>
            </CompositeCollection>
        </TabControl.ItemsSource>
    </TabControl>

Code behind:

cc.Collection=yourObservableCollection
Community
  • 1
  • 1
The One
  • 4,560
  • 5
  • 36
  • 52
-2

Unfortunately you can't mix ItemsSource binding with explicitly added Items collection objects. So you have two choices, either add fixed items and then items from your bound list manually to the Items collection or bind ItemsSource to a collection that contains both a set of fixed objects and your bound collection's items. In either case the biggest issue is probably with updating when your data changes- making sure the right items get removed/added and the UI updates correctly.

John Bowen
  • 24,213
  • 4
  • 58
  • 56