Categories
.NET Windows

Titlebars and Themes: A WinUI Adventure

I’m working on a WinUI 3 app at the moment and I’ve been trying to match the in-box titlebar behaviour for Windows 11. Anyone who has experience with Windows will immediately respond “Which One?” as various apps within Windows look different due to “reasons”.

My starting point is to match the Settings app, as this is a key part of built-in functionality. Of course during the course of this investigation I noticed that File Explorer and Terminal behave differently even though they are modern in-box apps. Legacy Win32 apps and Edge have a completely different look, let’s just ignore that for now.

Firstly I want to do as little as possible, so I have a WinUI 3 app with the default behaviour and have changed the titlebar to be custom drawn by the app following the documentation. It’s a good start and combining that with applying mica results in a modern looking window at a cursory glance.

The problem I noticed is that the window buttons (minimize, maximize and close) default to theme accent colour and are quite jarring compared with Windows settings. The first step therefore was to set the background colour of these to transparent (but not change the hover colours so that they highlight when prodded by the user). However the foreground colour doesn’t change with the theme so these can end up disappearing if you switch themes!

The CommunityToolkit has a ThemeWatcher to raise an event at this point – could we just use that and change the text colours? No it turns out it doesn’t fire in a WinUI 3 project. However you can use the UISettings.ColorValuesChanged event instead. This fires both when changing theme colours and switching between light and dark. So now, we just need to have a reference to the AppWindowTitleBar and we can change the text colours when this event fires. I also found that when trying to capture a video of this in action it would regularly fail to update properly leaving it stuck in some limbo state – presumably something in the capture code for GameBar or the Snipping tool is swallowing the window messages used to trigger the change. However I’ve capture an image of the titlebar in the four states so you can see the difference. If you squint at it and cross your eyes you can probably recreate the transition in some kind of magic eye style.

Screenshot of title bars in inactive and active states on light and dark themes.

The key things I had to set beyond the defaults to get it to look consistent were:-

ButtonBackgroundColor = Colors.Transparent;
ButtonInactiveBackgroundColor = Colors.Transparent;
ButtonInactiveForegroundColor = (Windows.UI.Color)App.Current.Resources["SystemAccentColor"];

On theme change I check the current theme and if IsCustomizationSupported() set the ButtonForegroundColor to Black for Light and White for Dark theme. Ideally these should use a theme color too so that they correctly adjust to other customisations.

Because of some odd behaviour (possibly due to screen recording) I found that changing the title text would make it less likely to fail to update. However with further testing I might not need to do that, and just ignore the screen recording quirk.

The Terminal and File Explorer approach uses a darker (or lighter on dark theme) colour when you hover over a button, rather than the accent colour. It’s more subtle and I’m not sure which theme resource these colours are derived from, so I’m not going to change this for now.

Preview Window

The second issue, which was with the preview window on the taskbar rather than the app window itself, was that it was showing a generic icon, even though the app icons were all setup correctly. I had missed a step, and Nick Randolph showed me what I’d forgotten. As well as setting the images for your package, you also need to set an icon for the AppWindow. This is a traditional .ico file which can contain multiple sizes and colour depths. Once I’d set this in the constructor for my MainWindow (this app only uses a single window) it was showing correctly in the preview.

m_AppWindow = GetAppWindowForCurrentWindow();
m_AppWindow.SetIcon("AppIcon.ico");

AppIcon.ico is in the root folder of the project with a build type of Content. It’s important to note that .ico files don’t have separate light and dark theme images so this needs to be something which works well with either theme. Looking through some in-box examples they are a bit of a mixed bag.

By Peter Foot

Microsoft Windows Development MVP

One reply on “Titlebars and Themes: A WinUI Adventure”

Comments are closed.