Observing system color changes in UWP apps

WinUI

7 years ago

Users love personalization and expect apps to adapt to their choices. For this purpose we can use the accent color. But what if the user changes settings at runtime?

ThemeResource

XAML has a concept of ThemeResource markup extension. This allows us to dynamically reference resources based on the currently selected system theme. Anytime the user changes the accent color or switches between dark and light mode, the resource is updated. To access the current accent color, we can use the SystemAccentColor key.

<Grid>
   <Grid.Background>
      <SolidColorBrush Color="{ThemeResource SystemAccentColor}" />
   </Grid.Background>
</Grid>

For convenience there is also a accent color-based SolidColorBrush resource, under the not-so intuitive SystemControlHighlightAccentBrush key.

<Grid Background="{ThemeResource SystemControlHighlightAccentBrush}" />

Observing changes

Although using ThemeResource will be sufficient for most cases, sometimes we need to be notified about color changes outside of XAML. In such cases the UISettings class comes in handy. In stark contrast to most other similar Windows Runtime APIs, the UISettings do not provide a Current singleton instance property nor a GetForCurrentView factory method, but just have a simple constructor. Apart from many properties that let us query various UI related information (CaretBlinkRate , CursorSize or even HandPreference ) it also has two useful events: TextScaleFactorChanged and ColorValuesChanged . Whereas the former event deals with text scaling, the latter event is fired whenever the system color settings are changed.

UISettings uiSettings = new UISettings();
uiSettings.ColorValuesChanged += ColorValuesChanged;

In the event handler we can check the accent color either by directly looking up the Resources dictionary or by the GetColorValue method:

Color accentColor = sender.GetColorValue(UIColorType.Accent);
//or
Color accentColor = (Color)Resources["SystemAccentColor"];

Accessing the light/dark mode setting is a little less intuitive. We must query the UIColorType.Background color and check if it is black or white:

var backgroundColor = sender.GetColorValue(UIColorType.Background);
var isDarkMode = backgroundColor == Colors.Black;

Warning: the event is not always reliable. While it usually fires several times for each accent color change, sometimes the event doesn't fire at all. I am afraid this is a bug and there is not much we can do about it.

Update (2019)

There are two additional suggestions to improve the reliability of this event, thank you @aasgenirb235 and @Colin for noting them in the comments:

  • It is important to store a reference to the UISettings instance as a field in your class, otherwise the instance will get collected and no events will be delivered (the event subscription is weak only)
  • The color change event handler should run any UI-related code with a dispatcher, as the changes not always fire on the UI thread

Example code

I have prepared a sample project that demonstrates the use of UISettings.ColorValuesChanged event including accent color and light/dark mode. The solution is available on my GitHub.