views:

3232

answers:

8

I'm trying to change the foreground text colour of a WPF tab item's header textblock using triggers. This works fine for most (simpler) scenarios but not when TextBlocks have been globally styled.

So this simple "mouse over" trigger would work in terms of changing the foreground color:

<Style x:Key="testTabItemStyle1" TargetType="{x:Type TabItem}">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" Value="White"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
   <Setter.Value>
     <ControlTemplate TargetType="{x:Type TabItem}">
       <Grid SnapsToDevicePixels="true">
         <Border x:Name="Bd" Background="White" BorderBrush="Gray" BorderThickness="1,1,1,0">
            <ContentPresenter HorizontalAlignment="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" x:Name="Content" VerticalAlignment="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" ContentSource="Header"/>
         </Border>
       </Grid>
       <ControlTemplate.Triggers>
         <Trigger Property="IsMouseOver" Value="true">
           <Setter Property="Background" TargetName="Bd" Value="Black"/>
           <Setter Property="Foreground" Value="False"/>
         </Trigger>
       </ControlTemplate.Triggers>
     </ControlTemplate>
   </Setter.Value>
</Setter>
</Style>

The problem is that when TextBlocks are globally styled in App.xaml (for maintaining a consistent look), the foreground does not change, but retains the globally styled foreground color. This is how my TextBlocks are styled:

    <Style TargetType="{x:Type TextBlock}">
        <Setter Property="FontFamily" Value="Arial"/>
        <Setter Property="Foreground" Value="Brown"/>
        <Setter Property="Margin" Value="4,0,4,0"/>
        <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
        <Setter Property="TextWrapping" Value="NoWrap"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
    </Style>

So my question is shouldn't the explicitly defined style assignment (in TabItem's trigger) have precedence? More importantly, how do I work around this without assigning styles to all my textblocks individually but having the TabItem textblock change color as expected?

Many thanks

NT

A: 

Works for me. Just had to change this:

<Setter Property="Foreground" Value="False"/>

to this:

<Setter Property="Foreground" Value="White"/>

HTH, Kent

Kent Boogaart
A: 

Thanks for the reply. Unfortunately this was a shortened version, I'm still having problems in the original, the expected color here is Red when tab item is disabled:

    <SolidColorBrush x:Key="TabControlNormalBorderBrush" Color="#8C8E94"/>
    <LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
     <GradientStop Color="#F3F3F3" Offset="0"/>
     <GradientStop Color="#EBEBEB" Offset="0.5"/>
     <GradientStop Color="#DDDDDD" Offset="0.5"/>
     <GradientStop Color="#CDCDCD" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="TabItemHotBackground" EndPoint="0,1" StartPoint="0,0">
     <GradientStop Color="#EAF6FD" Offset="0.15"/>
     <GradientStop Color="#D9F0FC" Offset=".5"/>
     <GradientStop Color="#BEE6FD" Offset=".5"/>
     <GradientStop Color="#A7D9F5" Offset="1"/>
    </LinearGradientBrush>
    <SolidColorBrush x:Key="TabItemSelectedBackground" Color="#F9F9F9"/>
    <SolidColorBrush x:Key="TabItemHotBorderBrush" Color="#3C7FB1"/>
    <SolidColorBrush x:Key="TabItemDisabledBackground" Color="#F4F4F4"/>
    <SolidColorBrush x:Key="TabItemDisabledBorderBrush" Color="#FFC9C7BA"/>
    <Style x:Key="testTabItemStyle1" TargetType="{x:Type TabItem}">
     <Setter Property="FocusVisualStyle" Value="{StaticResource TabItemFocusVisual}"/>
     <Setter Property="Foreground" Value="Black"/>
     <Setter Property="Padding" Value="6,1,6,1"/>
     <Setter Property="BorderBrush" Value="{StaticResource TabControlNormalBorderBrush}"/>
     <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
     <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
     <Setter Property="VerticalContentAlignment" Value="Stretch"/>
     <Setter Property="Template">
      <Setter.Value>
       <ControlTemplate TargetType="{x:Type TabItem}">
        <Grid SnapsToDevicePixels="true">
         <Border x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" Padding="{TemplateBinding Padding}">
          <ContentPresenter HorizontalAlignment="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" x:Name="Content" VerticalAlignment="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True"/>
         </Border>
        </Grid>
        <ControlTemplate.Triggers>
         <Trigger Property="IsMouseOver" Value="true">
          <Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemHotBackground}"/>
         </Trigger>
         <Trigger Property="IsSelected" Value="true">
          <Setter Property="Panel.ZIndex" Value="1"/>
          <Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemSelectedBackground}"/>
         </Trigger>
         <MultiTrigger>
          <MultiTrigger.Conditions>
           <Condition Property="IsSelected" Value="false"/>
           <Condition Property="IsMouseOver" Value="true"/>
          </MultiTrigger.Conditions>
          <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemHotBorderBrush}"/>
         </MultiTrigger>
         <Trigger Property="TabStripPlacement" Value="Bottom">
          <Setter Property="BorderThickness" TargetName="Bd" Value="1,0,1,1"/>
         </Trigger>
         <Trigger Property="TabStripPlacement" Value="Left">
          <Setter Property="BorderThickness" TargetName="Bd" Value="1,1,0,1"/>
         </Trigger>
         <Trigger Property="TabStripPlacement" Value="Right">
          <Setter Property="BorderThickness" TargetName="Bd" Value="0,1,1,1"/>
         </Trigger>
         <MultiTrigger>
          <MultiTrigger.Conditions>
           <Condition Property="IsSelected" Value="true"/>
           <Condition Property="TabStripPlacement" Value="Top"/>
          </MultiTrigger.Conditions>
          <Setter Property="Margin" Value="-2,-2,-2,-1"/>
          <Setter Property="Margin" TargetName="Content" Value="0,0,0,1"/>
         </MultiTrigger>
         <MultiTrigger>
          <MultiTrigger.Conditions>
           <Condition Property="IsSelected" Value="true"/>
           <Condition Property="TabStripPlacement" Value="Bottom"/>
          </MultiTrigger.Conditions>
          <Setter Property="Margin" Value="-2,-1,-2,-2"/>
          <Setter Property="Margin" TargetName="Content" Value="0,1,0,0"/>
         </MultiTrigger>
         <MultiTrigger>
          <MultiTrigger.Conditions>
           <Condition Property="IsSelected" Value="true"/>
           <Condition Property="TabStripPlacement" Value="Left"/>
          </MultiTrigger.Conditions>
          <Setter Property="Margin" Value="-2,-2,-1,-2"/>
          <Setter Property="Margin" TargetName="Content" Value="0,0,1,0"/>
         </MultiTrigger>
         <MultiTrigger>
          <MultiTrigger.Conditions>
           <Condition Property="IsSelected" Value="true"/>
           <Condition Property="TabStripPlacement" Value="Right"/>
          </MultiTrigger.Conditions>
          <Setter Property="Margin" Value="-1,-2,-2,-2"/>
          <Setter Property="Margin" TargetName="Content" Value="1,0,0,0"/>
         </MultiTrigger>
         <Trigger Property="IsEnabled" Value="false">
          <Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemDisabledBackground}"/>
          <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemDisabledBorderBrush}"/>
          <Setter Property="Foreground" Value="Red"/>
         </Trigger>
        </ControlTemplate.Triggers>
       </ControlTemplate>
      </Setter.Value>
     </Setter>
    </Style>

The global TextBlock style is still:

<Style TargetType="{x:Type TextBlock}">
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="Foreground" Value="Brown"/>
    <Setter Property="Margin" Value="4,0,4,0"/>
    <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
    <Setter Property="TextWrapping" Value="NoWrap"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
</Style>

And the TabControl code:

 <TabControl VerticalAlignment="Stretch" IsSynchronizedWithCurrentItem="True" BorderBrush="{x:Null}" Background="{x:Null}" Grid.Column="0" d:LayoutOverrides="HorizontalAlignment, Height" Margin="0,0,-233,0" MinWidth="440" x:Name="TabPages">
  <TabItem Header="Summary" x:Name="TabSummary" IsSelected="True" Style="{DynamicResource testTabItemStyle1}">
   <Border x:Name="TabSummaryBody" Margin="-5,-5,-5,-5">
    <StackPanel Margin="0,30,0,0" HorizontalAlignment="Center">
     <TextBlock Text="Please select a document using the tree view on your right to show its properties." FontSize="16"/>
    </StackPanel>
   </Border>
  </TabItem>
  <TabItem Header="Modify" x:Name="TabModify" IsEnabled="False" Style="{DynamicResource testTabItemStyle1}">
   <Border x:Name="TabModifyBody" Margin="-5,-5,-5,-5"/>
  </TabItem>
  <TabItem Header="Properties" x:Name="TabProperties" IsEnabled="False" Style="{DynamicResource testTabItemStyle1}">
   <Border x:Name="TabPropertiesBody" Margin="-5,-5,-5,-5"/>
  </TabItem>
  <TabItem Header="History" x:Name="TabHistory" IsEnabled="False" Style="{DynamicResource testTabItemStyle1}">
   <Border x:Name="TabHistoryBody" Margin="-5,-5,-5,-5"/>
  </TabItem>
  <TabItem Header="Access control" x:Name="TabAccessControl" IsEnabled="False" Style="{DynamicResource testTabItemStyle1}">
   <Border x:Name="TabAccessControlBody" Margin="-5,-5,-5,-5"/>
  </TabItem>
  <TabItem Header="Links" x:Name="TabLinks" IsEnabled="False" Style="{DynamicResource testTabItemStyle1}">
   <Border x:Name="TabLinksBody" Margin="-5,-5,-5,-5"/>
  </TabItem>
  <TabItem Header="Notifications" x:Name="TabNotifications" IsEnabled="False" Style="{DynamicResource testTabItemStyle2}">
   <Border x:Name="TabNotificationsBody" Margin="-5,-5,-5,-5"/>
  </TabItem>
 </TabControl>

If you can get this to work then it is either my OS (Vista x64 Ultimate here) or I don't know what.

Regards

NT

A: 

The key issue is the foreground property. I can successfully change FontWeight, Background and any other property I can think of.

But triggering a change to the Foreground property simply does not work during run-time (oddly enough both the Blend designer and VS 2008 designer respond successfully to any changes at design time)

I have also seen some articles here at StackOverflow where people say it works, but they all use XP, I use Vista Ultimate x64.

How strange...

A: 

You are setting the foreground color of a TabItem to Red, not the TextBlock. Maybe the TextBox style is not inherited from TabItem because user set implicit styles have precedence over trigger setters.

Try adding a binding to the TextBlock's parent TabItem Foreground property.

EDIT

Like this

Foreground="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}, AncestorLevel=1}, Path=Foreground}"
kek444
A: 

Hi Kek

Clearly this is the right answer, but the solution eludes me.

I can't see how I can do that because there is no TextBlock in the Template and the ContentPresenter which creates it has no Foreground property.

I'm curious how would you go about doing that.

Edited my answer. Hope it helps.
kek444
A: 

That makes sense but there is no Foreground property on ContentPresenter. The TextBlock I assume is created by the ContentPresenter, so I cannot add to it the TextBlock's foreground style: I have no TextBlock declared! This is why I attempted to alter its parent (TabItem)

You can see there is no TextBlock in the template declaration:

<ControlTemplate TargetType="{x:Type TabItem}">
   <Grid SnapsToDevicePixels="true">
      <Border x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" Padding="{TemplateBinding Padding}">
         <ContentPresenter HorizontalAlignment="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" x:Name="Content" VerticalAlignment="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True"/>
      </Border>
   </Grid>

Thus far I understand that I must somehow change that TextBlock's ForeGround, but how do I access it? Perhaps through code-behind rather than declaratively in XAML?

A: 

What I meant was this:

<TabItem Header="Summary" x:Name="TabSummary" IsSelected="True" Style="{DynamicResource testTabItemStyle1}">
     <Border x:Name="TabSummaryBody" Margin="-5,-5,-5,-5">
             <StackPanel Margin="0,30,0,0" HorizontalAlignment="Center">
                   <TextBlock Text="Please select a document using the tree view on your right to show its properties." 
                              FontSize="16"
                              Foreground="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}, AncestorLevel=1}, Path=Foreground}"/>
             </StackPanel>
     </Border>
</TabItem>

Binding finds the parent TabItem and binds to its Foreground property.

kek444
A: 

Hi Kek,

Many thanks for your help, you successfully steered me to the right direction.

My intention was to alter the TabItem's text (Created by WPF's ContentPresenter) as opposed to the TextBlock within the tab which is declared in XAML and can easily change colour.

The problem was with the global style taking precedence. And as the TextBlock is created by WPF rather than declared by me, I could not access it.

The solution was to specify the ContentPresenter Resources, as such:

<ControlTemplate TargetType="{x:Type TabItem}">
 <Grid SnapsToDevicePixels="true">
  <Border x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" Padding="{TemplateBinding Padding}">
   <ContentPresenter HorizontalAlignment="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" x:Name="Content" VerticalAlignment="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header" RecognizesAccessKey="True">
   <ContentPresenter.Resources>
    <Style TargetType="{x:Type TextBlock}">
         <Setter Property="Foreground" Value="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"/>
    </Style>
   </ContentPresenter.Resources>
  </ContentPresenter>
  </Border>
 </Grid>

As you can see I have set the TextBlock style within the ContentPresenter resources. So obviously now any TextBlocks within the ContentPresenter shall use the parent's Foreground property and this will take precedence due to value coercion, solving my problem.

Many thanks to all,

NT