I don't know about you but I love macOS' dark mode. Xojo 2018r3 onwards exposes an
Application.AppearanceChanged event that is fired whenever the user switches between light and dark mode in system preferences. The trouble is, it doesn't work
at all as expected on Windows (and possibly, Linux although I haven't checked).
It turns out that there's a dark mode (of sorts) on Windows that can be found in the Personalisation section of Settings. I wanted the app I'm working on to honour dark mode if the user has it enabled on their system and I wanted it to immediately switch modes when the user does so.
After several hours of messing around I came up with a solution. This solution relies on MonkeyBread Software's MBS Win Plugin, specifically the
WinNotificationMBS class. Essentially, I tell a subclass of
WinNotificationMBS to listen for a specific message broadcast by Windows (
WM_SETTINGCHANGE). This message is broadcast whenever the registry is altered. When my subclass receives this message it fires its
GotNotification event. In this event I check the value of a specific registry key (
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme). If its value is
0 then Windows is in dark mode, if it's
1 then it's in light mode. I then update my UI accordingly.
WinNotificationMBS. Call it
- Add a boolean property to the subclass called
- Add an Integer constant to the subclass called
WM_SETTINGCHANGEand set it to
- Create the following Constructor for the subclass:
Public Sub Constructor() // Calling the overridden superclass constructor. Super.Constructor // Listen for changes to the system registry. Call Me.ListenForMessage(WM_SETTINGCHANGE) // Determine if Windows is in dark mode or not. Try Dim reg As New _ RegistryItem("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", False) For i As Integer = 0 To reg.KeyCount - 1 If reg.Name(i) = "AppsUseLightTheme" Then If reg.Value(i) = 0 Then mDarkMode = True Else mDarkMode = False End If End If Next i Catch e As RegistryAccessErrorException mDarkMode = False End Try End Sub
Add an event definition called
AppearanceChanged(darkMode As Boolean)to
WindowsMessageHandler. We will fire this whenever the user switches between light and dark mode and we will pass in the mode type.
Add the following code to the
Select Case Message Case WM_SETTINGCHANGE // A system wide setting has been changed. // Has the default app mode changed (light/dark mode)? Try Dim reg As New _ RegistryItem("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize") For i As Integer = 0 To reg.KeyCount - 1 If reg.Name(i) = "AppsUseLightTheme" Then // Only update the appearance if the key has changed since the last known state. Var currentlyInDarkMode As Boolean = If(reg.Value(i) = 0, True, False) If currentlyInDarkMode <> mDarkMode Then mDarkMode = currentlyInDarkMode // Fire our custom event. AppearanceChanged(mDarkMode) End If End If Next i End Try End Select
- Add a new method to the
UpdateAppearance(sender As Object, darkMode As boolean). This is where you put the logic to handle light/dark mode changes.
- Add an instance of
Appclass. We'll call it
- In the
App.Openevent, instantiate our message handler:
#If TargetWindows // Setup our Windows message handler. This will listen for changes to the registry that // will tell us that the user has switched between light and dark mode. WindowsMessages = New WindowsMessageHandler // Intercept the `AppearanceChanged` event that the message handler fires and direct to our custom method: AddHandler WindowsMessages.AppearanceChanged, AddressOf UpdateAppearance #EndIf
Hopefully this will save a few people some time.