2
votes

Currently I'm working on a project of mine. I'm using tabs which can be closed / opened( though they are not exactly same as chrome's). The tab item's data template has a label and a button. Here is my code.

<Grid Name="grid_test" Margin="0,73,0,0" >
        <TabControl Background="WhiteSmoke"  FontFamily="Segoe UI" FontSize="12" Margin="0" Name="tabDynamic" ItemsSource="{Binding}" GotFocus="tabControl_GotFocus">

            <TabControl.ItemContainerStyle>
                <Style TargetType="TabItem">
                    <Setter Property="HorizontalAlignment" Value="Left" />
                    <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                    <Setter Property="Padding" Value="0"/>
                    <Setter Property="Height" Value="30"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="TabItem">

                                <Grid Name="Panel">
                                    <Border Name="Border" Margin="0,0,0,0" Background="Transparent"
              BorderBrush="#999999" BorderThickness="0,1,1,0" >
                                        <ContentPresenter x:Name="ContentSite"
                                    VerticalAlignment="Center"
                                    HorizontalAlignment="Stretch"
                                    ContentSource="Header"
                                    Margin="7,2"/>
                                    </Border>
                                </Grid>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsSelected" Value="True">
                                        <Setter TargetName="Panel" Property="Background" Value="White" />

                                    </Trigger>
                                    <Trigger Property="IsSelected" Value="False">
                                        <Setter TargetName="Panel" Property="Background" Value="#B3B3B3" />
                                    </Trigger>
                                    <Trigger Property="IsMouseOver" Value="True">
                                        <Setter TargetName="Panel" Property="Background" Value="#F6F6F6" />
                                    </Trigger>



                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>




            </TabControl.ItemContainerStyle>
            <TabControl.Resources>

                <DataTemplate x:Key="TabHeader" DataType="TabItem">
                    <Grid >
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="30"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <!--<TextBlock Margin="0,0,10,0"  HorizontalAlignment="Left" Text="{Binding RelativeSource={RelativeSource AncestorType=TabItem }, Path=Header}" />-->
                        <Label Width="100" Grid.Column="0" Margin="0,0,0,0" Height="32" HorizontalAlignment="Left" Content="{Binding RelativeSource={RelativeSource AncestorType=TabItem }, Path=Header}" />

                        <Button Grid.Column="1" HorizontalAlignment="Right" Name="btnDelete" Margin="0,0,0,0" Padding="0" Click="btnDelete_Click" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Name}"
                        Style="{StaticResource btnCustom}" Height="12" Width="12"/>
                    </Grid>
                </DataTemplate>



            </TabControl.Resources>
        </TabControl>
    </Grid>

Now this is what is the case when my number of tabs are limited. Please see the image provided in the link enter image description here

And this is what is happening when I'm adding in more and more tabs. Please see the image provided in the link enter image description here

I've already checked out these links - How to design tabs like Google Chrome tabs? and Tab control like Google Chrome tabs? , but they make no sense to me because I'm new to WPF and I'm trying to learn it properly. For now I only want to shrink the size of my tabItem when more of them are added. I would appreciate any kind of help.

UPDATE: I figured out the problem in my code and followed the suggestions of @Paparazzi and @Chris W. . My code is working fine but my actual problem is still not solved :( I added some msdn code suggested by @Chris W. in the

<TabControl.Resources>

I added a viewbox in the required place. But, this code with viewbox shows a weird behaviour. When my first tab is loaded, it is filling up the whole screen. Size of the tabs are gradually decreasing(in all dimensions) with increasing amount of tabs.

<TabControl.Resources>
                <Style  TargetType="{x:Type TabControl}">
                    <Setter Property="OverridesDefaultStyle"
      Value="True" />
                    <Setter Property="SnapsToDevicePixels"
      Value="True" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type TabControl}">
                                <Grid KeyboardNavigation.TabNavigation="Local">
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto" />
                                        <RowDefinition Height="*" />
                                    </Grid.RowDefinitions>

                                    <Viewbox Stretch="Uniform" VerticalAlignment="Top" HorizontalAlignment="Left" >
                                     <TabPanel x:Name="HeaderPanel"
                                                Grid.Row="0"
                                                Panel.ZIndex="1"
                                                Margin="0,0,4,-1"
                                                IsItemsHost="True"
                                                KeyboardNavigation.TabIndex="1"
                                                Background="Transparent" />
                                    </Viewbox>
                                    <Border x:Name="Border"
                                              Grid.Row="1"
                                              BorderThickness="1"
                                              Background="Transparent"
                                              CornerRadius="2"
                                              KeyboardNavigation.TabNavigation="Local"
                                              KeyboardNavigation.DirectionalNavigation="Contained"
                                              KeyboardNavigation.TabIndex="2">

                                        <ContentPresenter x:Name="PART_SelectedContentHost"
                          Margin="4"
                          ContentSource="SelectedContent" />
                                    </Border>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>

               //...more code


            </TabControl.Resources>

B.t.w the solution suggested by @Paparazzi (i.e usage of scrollviewer from this post Make TabControl Headers Scrollable in WPF )is working fine. This is the code inside TabControl.Resources

<ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Auto" VerticalAlignment="Top" HorizontalAlignment="Left" >
      <TabPanel x:Name="HeaderPanel" Grid.Row="0" Panel.ZIndex="1"
               Margin="0,0,4,-1"
               IsItemsHost="True" KeyboardNavigation.TabIndex="1"
               Background="Transparent" />
  </ScrollViewer>
1
I'd probably have to see more of your stuff to tell whether you'd have anything in there to restrict it but if you were to apply this old answer around an ItemsPresenter (your TabItems host panel) it should do what you're hoping pretty easily.Chris W.
I'd try your answer. Thank you for that. Again, can I add only the tabItems to a viewbox, I mean viewbox should not contain, the content of the tab item, it should have only the tabitem(with header and button)? B.t.w what do you actually want to see? It would be better if you try to be more specific, I'll post it if needed :) . B.t.w, I'm adding the tab items using the back end code.kots_14
Yea no worries. So if we look at a default template for TabControl we see a TabPanel named x:Name="HeaderPanel" that has the property IsItemsHost="True" which act's as the parent panel for your tabs. If we made that our ViewBox then it would load whatever items you throw at it in there. There's several ways to accomplish the same effect it just depends on your layout goals.Chris W.
I tried using viewbox in the dataTemplate section, but it didn't work.kots_14
I'm checking your latest suggestion. Thanks :)kots_14

1 Answers

2
votes

It appears the label in your tabItem dataTemplate has a fixed width of 100. Rather than fixing this width you could bind it to a property in your code-behind that held a count of the number of tab items (e.g. "TabItemCount"). This on its own is not useful because you obviously don't want a tab with a width of 1 and the more tabs you have the the bigger they would get which is the opposite of the behavior you probably want.

To solve this you would need a converter which is a class that wpf bindings can use to take any input value/type and return another type/value.

The binding would then look something like this;

        <Window.Resources>
            <local:CountToWidthConverter x:Key="CountToWidthConverter" />
    </Window.Resources>

...

  <Label Width="{Binding TabItemCount, Converter={StaticResource CountToWidthConverter}}" ...

The converter would look something like this;

public class CountToWidthConverter : IValueConverter
    {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                 var tabCount = (int)value;
                 var tabWidth = 1000/tabCount;
                 return tabWidth;
            }

            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {

                 ...

            }
    }

If the number of tabs is just 1, then the width of the label would be 1000, but as the number of tab items increased the width of each item would decrease. You could then set a maximum width for all tabs and then just divide that up among the tabs you create. The code in the converter class can obviously be made to do anything you like so you could handle all kinds of special cases. You can actually bind to any object type you like and it could be possible to make it work so long as there was some connection between the object you passed in and the required width of the tabs.

Converters are great for changing properties such as visibility, color etc where you want want the property value to change based on some condition such as making text turn red if it becomes negative.