Toggling Light and Dark Mode

Does anyone know of any windows API’s to toggle an applications Dark / light mode flag during runtime.
It is possible on macOS to ask just the running app to change all of its dialogs into light or dark mode using NSAppearence. I was wanting to support this on Windows.

Last time I checked (admittedly a long time ago), there was no exposed API for this, but the registry key propagates immediately.

Private Sub setWindowsDarkMode(value as Boolean)
    dim r as new RegistryItem( "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", False )
    if IsNull( r ) then Return
    dim newValue as Int32 = If( value, 0, 1 )
    r.Value( "AppsUseLightTheme" ) = newValue
  #endif
End Sub
1 Like

I was wanting to change just the application running. I think this just toggles the entire system to light / dark mode?

It does toggle the entire system. There is, I believe, a declare for setting just the application. I’ll see if I can dig it up. I’m sure it’s in my GraffitiImmersive source code that became obsolete when Xojo implemented dark mode.

2 Likes

Try this. I’m not sure how it’ll behave if called after App.Opening, but it’s worth a shot.

#if TargetWindows then
  soft declare function SetPreferredAppMode lib "uxtheme.dll" Alias "#135" (mode as Integer) as Integer
  call SetPreferredAppMode( 1 )
#endif

The API’s enum looks like this:

enum class PreferredAppMode
{
   Default, // This would be 0
   AllowDark, // 1
   ForceDark, // 2
   ForceLight, // 3
   Max // 4
};
1 Like

Thinking more about this, depending on how it was implemented, switching at runtime for just the app may not work in Xojo. I don’t know enough about how the framework was modified to accomplish dark mode on Win32 controls. Maybe @William_Yu can help us out…

This isn’t “toggling”, but with this you can set a boolean in your Options, to ignore the system setting. Let’s call it ‘ignoreDarkMode’.

Then, in App.Open:

#if TargetWindows
if ignoreDarkMode then
System.EnvironmentVariable("XOJO_WIN32_DARKMODE_DISABLED")="True"
end if
#Endif

With that saved to your preferences file, the change will take effect when the app is re-launched.

Meanwhile, Anthony’s solution just came in as I was typing, and I bet it’s better :slight_smile:

1 Like

Yeah, that’ll work to disable dark mode, but definitely won’t provide the toggling. At least not without an application restart. Which may be the way to go.

1 Like

William was helpful, a while back, in offering the declares to do this in macOS. I’ve implemented that in my projects (and given him credit. :slight_smile: )

Here’s another declare I was using for controlling the mode of a window. You can call this any time.

soft declare function AllowDarkModeForWindow Lib "uxtheme.dll" Alias "#133" (hWnd as ptr, bAllow as Boolean ) as Integer

Use:

call AllowDarkModeForWindow(windowInstance.Handle, False)

And you can try this for setting the window as well.

Private Sub setAppDarkMode(Mode as Int32)
  Soft Declare Function DwmSetWindowAttribute Lib "dwmapi.dll" ( hWnd as Ptr, dwAttribute as UInt32, pvAttribute as Ptr, cbAttribute as Uint32 ) as Integer
  dim mb as new MemoryBlock(4)
  mb.Int32Value(0) = Mode
  dim result as Integer = DwmSetWindowAttribute( self.Handle, 19, mb, mb.Size )
End Sub

I had a LOT of time invested in dark mode for Windows to get things to behave properly within the Xojo context. That means tons of code to affect different aspects. Not all of which makes sense so many years after the fact.

My desktop app has a custom rendered main GUI which can have a light or dark theme. Once light and dark mode came along for macOS and Windows I wanted to add a menu to control the Theme - Auto / Light / Dark. Which at least changes the main GUI, but being able to change the dialogs etc and main window’s menubar etc on windows would be the best solution.

I am trying out your suggestions now

Tried all sorts of things, nothing seems to be working. I guess I just leave it to be controlled by system setting.

I haven’t managed much either, but this is on my M1 laptop, debugging to my Windows ARM VM. I’ll try again on my Intel machine, and that VM, and let you know if I find something.

Meanwhile, the basic “ignore dark mode” , that I presented above, is pretty foolproof. If your project is already set up to save user settings, it’s easy.

Edit: “version of Xojo” may be useful information, too. I’m using 2024r1.1 on Sonoma 14.4.1.

There is some code by Jürg Otter here that helped me get as close as I could come.

For MacOS it allows you to force the mode (although @Anthony_G_Cyphers I ran into a few GraffitiSuite controls that didn’t respect the setting, for example still drawing light controls if the system was in light mode but my app was forced to dark, until I updated GraffitiColors.SystemAware.IsDarkMode to return the value of the Xojo native Color.IsDarkMode, although if you’re supporting older Xojo versions I understand the necessity of this method). On Windows, this code allows you to force light mode but not dark.

Will definitely be giving the setAppDarkMode method above a try on Windows.

1 Like

Should’ve let me know. Would have been a simple fix with a big impact for those affected.

This was just yesterday! Kind of serendipitous that this thread appeared today.

1 Like

Good to know. Fixed for the next release at any rate.

1 Like