14
votes

I have a TabControl that I have restyled. The TabItem has a trigger that is fired when the TabItem is selected that changes the TabItem text to bold and green. The problem I have is that the text in the contents of the tab is also set to bold and green.

I can work around this by setting all of my controls in the tab content to be the color and font weight I want, but should I have to do this? So, I have to make sure that every textblock in the contents area has a style that sets the color to black and font weight normal.

How can I set the IsSelected part of the TabItem to show green, but leave the contents of the tab alone?

I've tried to set the foreground of the TabControl to black, but this doesn't work.

You will see from the code sample below that the text on the first tab is green, and I want it to be black, but without setting each control in the tab content.

Code Sample below:

    <Grid>
    <Grid.Resources>
        <!-- Tab item -->
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="MinWidth" Value="200"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Grid>
                            <Border Name="Border" Padding="5,2">
                                <ContentPresenter ContentSource="Header"/>
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Border.IsMouseOver" Value="True"/>
                                    <Condition Property="IsSelected" Value="False"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="FontWeight" Value="Bold"/>
                                <Setter Property="Foreground" Value="Black"/>
                            </MultiTrigger>

                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="Border.IsMouseOver" Value="False"/>
                                    <Condition Property="IsSelected" Value="False"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Foreground" Value="Black" />
                            </MultiTrigger>

                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Foreground" Value="Green"/>
                                <Setter Property="FontWeight" Value="Bold"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <!-- Tab control -->
        <Style  TargetType="{x:Type TabControl}">
            <Setter Property="SelectedIndex" Value="0"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabControl}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Border Grid.Column="0" Padding="5" Margin="0,0,5,0" CornerRadius="3">
                                <StackPanel Orientation="Vertical">
                                    <ScrollViewer VerticalScrollBarVisibility="Auto" FocusVisualStyle="{x:Null}">
                                        <TabPanel IsItemsHost="True"/>
                                    </ScrollViewer>
                                </StackPanel>
                            </Border>
                            <Border Grid.Column="1" BorderBrush="Black" BorderThickness="0">
                                <ScrollViewer VerticalScrollBarVisibility="Auto" FocusVisualStyle="{x:Null}" Padding="10,0">
                                    <ContentPresenter ContentSource="SelectedContent"/>
                                </ScrollViewer>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Grid.Resources>

    <TabControl Name="tabControl" TabStripPlacement="Left">
        <!-- First tab item -->
        <TabItem IsSelected="True">
            <TabItem.Header>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Profile"/>
                </StackPanel>
            </TabItem.Header>
            <TextBlock Text="Page 1 Sample Text with no foreground set." FontSize="30"/>
        </TabItem>

        <!-- Second tab item -->
        <TabItem IsSelected="True">
            <TabItem.Header>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Profile"/>
                </StackPanel>
            </TabItem.Header>
            <TextBlock Text="Page 2 Sample Text with foreground set manually." FontSize="30" Foreground="Red"/>
        </TabItem>
    </TabControl>
</Grid>
4
Post some code to find out what is wrong!decyclone
Added code sample as requested.Adrian

4 Answers

12
votes

I have just come across this very same problem, and after fiddling around with it a little I think I have found a more elegant solution.

I'm saying more elegant since it would leave the ContentPresenter intact, and apply the foreground and fontweight setters to the ContentPresenter's TextElement (which is basically an attached property, but that's beside the point).

The main advantage of this approach is that replacing the ContentPresenter with a TextBlock implicitly assumes that the header will only contain text, which limits the usability of the workaround and produces a less robust code. Leaving the ContentPresenter in place will allow any content, e.g. images+text.

One more thing you would have to do is name your ContentPresenter:

<Setter Property="Template">
     <Setter.Value>
         <ControlTemplate TargetType="{x:Type TabItem}">
             <Grid>
                <Border Name="Border" Padding="5,2">
                   <ContentPresenter x:Name="CP" ContentSource="Header"/>
                </Border>
             </Grid>
             <ControlTemplate.Triggers>
                <MultiTrigger>
                   <MultiTrigger.Conditions>
                      <Condition Property="Border.IsMouseOver" Value="True"/>
                      <Condition Property="IsSelected" Value="False"/>
                   </MultiTrigger.Conditions>
                     <Setter Property="TextElement.FontWeight" TargetName="CP" Value="Bold"/>
                     <Setter Property="TextElement.Foreground" TargetName="CP" Value="Black"/>                                
                </MultiTrigger>...

Now the Foreground and FontWeight will not be inherited by the contents of the TabItem (tested).

Enjoy :)

4
votes

This is pretty old, but I came across it while looking for an answer for a similar problem, and I found the supplied answers not at all helpful. Here is how I fixed this.

If you change the ContentPresenter to a TextBlock in the control template of your tabitem, like this:

....stuff above here...
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
   <Border Name="Border" Padding="5,2">
        <TextBlock x:Name="TabItemContent" Text="{TemplateBinding Header}"/>
   </Border>
</Grid>
... stuff below here....

Then in your trigger on that control template you specify the targetname in the IsSelected trigger..ie.

...stuff above here...
 <Trigger Property="IsSelected" Value="True">
     <Setter Property="Foreground" TargetName="TabItemContent" Value="Green"/>
     <Setter Property="FontWeight" Value="Bold"/>
</Trigger>
... stuff below here ...

That should give you green text when the tab is selected, and not green all the other times, while leaving the text coloring in the rest of the app alone.

3
votes

Unfortunately, if you set the Foreground (or FontWeight) on a ContentPresenter by some trigger, you still assume that the header will only contain Text.

If you set Header="SomeHeaderName" (i.e. text only), the ContentPresenter will generate a TextBlock to host this headertext; the ContentPresenter will be the (logical) parent of this TextBlock and so, the new Foreground set on the ContentPresenter will be inherited by this TextBlock. This works ok.

However, if the Header is assigned some piece of visual tree, like a horizontal StackPanel with an Image and a TextBlock (or even a single TextBlock), then the logical parent of the StackPanel is the TabItem and not the ContentPresenter. Inheritance works via the logical tree and so the TextBlock inside the StackPanel will ultimately inherit its Foreground from the TabItem again; the foreground that was set on the ContentPresenter does not have any effect on this.

One possible solution: a DataTemplate is eventually applied to a ContentPresenter, so the TemplatedParent of the root of the DataTemplate is the ContentPresenter; and inheritance works through a TemplatedParent-Child barrier as well. So if you are able to set the TabItem.HeaderTemplate instead of the TabItem.Header, then you can set the Foreground on the header's ContentPresenter, because the root of the HeaderTemplate will inherit the Foreground from the ContentPresenter. The SelectedContent however, will not because the Foreground is not set on the TabItem (and the content inherits its Foreground from the TabItem).

Hope this helps!

0
votes

Every control in wpf inherits properties from its parent. Since the TabItem's color was black, its child textblock was also of black color. Now since you've changed the entire TabItem's foreground color to green, all its children will inherit it.

Here you can set your TabItem.Header's or its contents' foreground to green, so that it will not affect other contents in the TabItem. Else you can invert the solution.

Else Try this:

<Window.Resources>
    <DataTemplate x:Key="greenHeaderTemplate">
        <StackPanel Orientation="Horizontal"> 
            <TextBlock Text="Profile" 
                       FontWeight="Bold" 
                       Foreground="Green"/> 
        </StackPanel> 
    </DataTemplate>
    <DataTemplate x:Key="defaultHeaderTemplate">
        <StackPanel Orientation="Horizontal"> 
            <TextBlock Text="Profile"/> 
        </StackPanel> 
    </DataTemplate>
</Window.Resources>

<Trigger Property="IsSelected" Value="True">      
    <Setter Property="HeaderTemplate" 
            Value="{StaticResource greenHeaderTemplate}"/> 
</Trigger>

<TabItem IsSelected="True" HeaderTemplate="{StaticResource defaultHeaderTemplate}"> 
    <TextBlock Text="Page 1 Sample Text with no foreground set." FontSize="30"/> 
</TabItem>