Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fluent2 TabView change selected tab background color to accent color #10356

Open
johanneskopf opened this issue Jan 29, 2025 · 8 comments
Open

Comments

@johanneskopf
Copy link

johanneskopf commented Jan 29, 2025

Description

I have a .NET 9 WPF application that I want to style using the new fluent (Windows 11) theme. I need to style the background of the selected tab of the TabView to use the theme accent color.

Reproduction Steps of the Problem

Find the sample repro that showcases the issue explained below here: https://github.com/johanneskopf/sample-selected-TabItem-background-override. Executing the sample shows this window (TabView on top, with a button inside the tab):

Image

Now when changing the theme color from
Image
to
Image

I get this result:
Image

I need the TabView to reflect the changes of the Windows theme accent color here. Thus, when the theme color is changed to blue, the selected tab item background should be green as well. Currently this only works for the button with the default style, not for the TabView's selected tab background in my sample.

Code explanation

I statically achieved what I want by doing the following, it is not reflected until the application is restarted though:

App.xaml:

<Application x:Class="TestApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="View/MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.xaml" />
                <ResourceDictionary Source="OverrideStyles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

OverrideStyles.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{Binding Source={StaticResource AccentButtonBackground}, Path=Color}" />
    <SolidColorBrush x:Key="TabViewItemForegroundSelected" Color="{Binding Source={StaticResource AccentButtonForeground}, Path=Color}" />
    <SolidColorBrush x:Key="TabViewSelectedItemBorderBrush" Color="{Binding Source={StaticResource AccentControlElevationBorderBrush}, Path=Color}"/>
</ResourceDictionary>

What I want to achieve is easily possible for buttons by the way. A button defined like this dynamically updates to the chosen theme color on-the-fly:

<Button HorizontalAlignment="Stretch" Style="{DynamicResource AccentButtonStyle}" Content="Test"/>

As a reference, here is the dark default style of the fluent theme:
https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/Themes/PresentationFramework.Fluent/Themes/Fluent.Dark.xaml

There, one can find those lines, which might be relevant:

<Style x:Key="DefaultTabItemStyle" TargetType="{x:Type TabItem}">
    <Setter Property="Background" Value="{DynamicResource TabViewItemHeaderBackground}" />
...
</Style>

<SolidColorBrush x:Key="AccentButtonBackground" Color="{StaticResource SystemAccentColorLight2}" />
@miloush
Copy link
Contributor

miloush commented Jan 29, 2025

If you expect the values to be changing dynamically, you need to be using DynamicResource rather than StaticResource

@johanneskopf
Copy link
Author

@miloush I tried that, but it didn't compile. Can you provide an example code?

@miloush
Copy link
Contributor

miloush commented Jan 29, 2025

Then you shouldn't be assuming the brushes are SolidColorBrushes and they are in fact not:

<LinearGradientBrush x:Key="AccentControlElevationBorderBrush" MappingMode="RelativeToBoundingBox" StartPoint="0,1" EndPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.025" Color="{StaticResource ControlStrokeColorOnAccentSecondary}" />
<GradientStop Offset="0.075" Color="{StaticResource ControlStrokeColorOnAccentDefault}" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>

So they don't have the Color property

@miloush
Copy link
Contributor

miloush commented Jan 29, 2025

If you simply want to use the accent color, you can use Color="{DynamicResource {x:Static SystemColors.AccentColorLight2Key}}" or similar. If you want to set a named brush to a different named brush, perhaps @robert-abeo can suggest the right way.

@johanneskopf
Copy link
Author

johanneskopf commented Jan 29, 2025

@miloush You are correct, AccentControlElevationBorderBrush is sometimes a LinearGradientBrush in the theme (see here) but AccentButtonBackground and AccentButtonForeground are a SolidColorBrush in all theme files. Verified using query a and query b

Also, I tried your suggestion, thus I changed my brush from
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{Binding Source={StaticResource AccentButtonBackground}, Path=Color}" />
to:
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{DynamicResource {x:Static SystemColors.AccentColorLight2Key}}" />
Have I understood your suggestion correctly?

I still run into the same problem with those changes. The initial value upon starting the application is the accent color, but upon changing the theme color this value is not updated:

Image

@robert-abeo
Copy link

robert-abeo commented Jan 29, 2025

You are correctly overriding the lightweight styling resources to customize the color. @miloush Is likely correct that the issue is simply you aren't indicating DynamicResource. StaticResource will NOT process change notifications and update.

You also shouldn't be binding and MUST use the base color resources rather than the brushes.

<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{DynamicResource SystemAccentColorLight2}" />
<SolidColorBrush x:Key="TabViewItemForegroundSelected" Color="{DynamicResource TextOnAccentFillColorPrimary}" />
    <LinearGradientBrush
        x:Key="TabViewSelectedItemBorderBrush"
        MappingMode="RelativeToBoundingBox"
        StartPoint="0,1"
        EndPoint="0,0">
        <LinearGradientBrush.GradientStops>
            <GradientStop Color="{DynamicResource ControlStrokeColorOnAccentSecondary}" Offset="0.025" />
            <GradientStop Color="{DynamicResource ControlStrokeColorOnAccentDefault}" Offset="0.075" />
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>

@johanneskopf
Copy link
Author

johanneskopf commented Jan 29, 2025

@robert-abeo
I tried your suggestion, and your code directly results in a crash (XAMLParseException) upon starting the application:
Image

Was able to narrow the problem down to this line:
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{DynamicResource SystemAccentColorLight2}" />
I put this in my OverrideStyles.xaml, and it crashes.

As I already wrote in my previous answer, using DynamicResource in the way suggested above (as I understood it) didn't fix the issue unfortunately

Thank you both for your answers so far @miloush @robert-abeo .

EDIT: Played around a bit and this line leads to an immediate crash in debug mode without showing any call stack, etc. after crashing, which is also very interesting.
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected" Color="{DynamicResource SystemAccentColor}" />

@robert-abeo
Copy link

@johanneskopf Please take a look at the actual resource files. It will help you a lot in the future. i.e. https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/Themes/PresentationFramework.Fluent/Resources/Theme/Dark.xaml

What I sent is untested and is only an example. Of course it might have issues and only communicates the idea. The goal is you should understand it well enough you don't need to copy/paste from others. That said, you shouldn't see the issues you are reporting.

It should work as shown here:

<SolidColorBrush x:Key="TabViewBackground" Color="{StaticResource SubtleFillColorTransparent}" />
<SolidColorBrush x:Key="TabViewForeground" Color="{StaticResource TextFillColorPrimary}" />

I would make sure you are placing them in <Application.Resources> or directly in a Control.Resources somewhere in the tree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

4 participants