Windows has had a dark mode feature for a long time now, however, as a Windows Forms and/or WPF (Windows Presentation Foundation) developer, you may have noticed that the Windows dark mode does not affect your app at all automatically - in fact the Windows dark mode only automatically affects UWP, WinUI and Chromium based user interfaces - there's no support for Windows dark mode in Windows Forms and WPF.
Tip: If you want to set the title bar's colours to any colours you want instead of just light or dark, follow this guide here.
Luckily, it is very easy to determine if dark mode is currently enabled and be notified any time dark mode is switched on or off - from there it's just a matter of updating your UI to match which mode Windows is in. While this may work fine for the client area of your app (i.e. the area of your app that does not include the title bar and border), you don't have a lot of control over the non-client area of your app and you'll notice that by default, the title bar and border of any Windows Forms or WPF window will not adjust to dark mode regardless of the user's dark mode settings. Luckily, Windows includes a function that you can use to tell the DWM (Desktop Window Manager), which is responsible for drawing your app windows' title bars, to draw the title bar and border in dark mode like so:
How to enable dark title bar
To set the title bar to dark mode, you need to use the '
DwmSetWindowAttribute' function from dwmapi.dll. This can be defined in
C# as:
using System.Runtime.InteropServices;
[DllImport("dwmapi.dll", PreserveSig = true)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref bool attrValue, int attrSize);
And in VB:
Imports System.Runtime.InteropServices
<DllImport("dwmapi.dll", PreserveSig:=True)>
Public Shared Function DwmSetWindowAttribute(hwnd As IntPtr, attr As Integer, ByRef attrValue As Boolean, attrSize As Integer) As Integer
End Function
Once you've defined the function, you can call it from your code to change various
DWMWINDOWATTRIBUTEs. Specifically, we want to change '
DWMWA_USE_IMMERSIVE_DARK_MODE' which has a value of 20. This value can be set as either true or false - when it's set to true, the title bar and border will be drawn in dark mode, and when set to false, the title bar will use the default behaviour (which currently defaults to white). Note that although Microsoft's documentation states that this attribute is only available in Windows 11, I have tested and it is available in Windows 10 as well (Though it is not available in older versions of Windows 10 - I'm not sure when exactly it was added).
So to set the title bar to dark mode in C#, just call the
DwmSetWindowAttribute with the following parameters (Note: '
System.Windows.Interop.WindowInteropHelper(this).Handle' assumes that this code is being called from a WPF Window class, if being called from a Windows Forms Form class, you can just use
this.Handle. If it's being called from somewhere else, just replace 'this' with a reference to the window, or replace that entire argument with your own code for getting the handle of the window - the handle is the unique identifier of a window which in this case is used to tell Windows what window you want to change the DWM attributes of.):
var value = true;
DwmSetWindowAttribute(new System.Windows.Interop.WindowInteropHelper(this).Handle, 20, ref value, System.Runtime.InteropServices.Marshal.SizeOf(value));
And call it like so in VB (Note: '
System.Windows.Interop.WindowInteropHelper(Me).Handle' assumes that this code is being called from a WPF Window class, if being called from a Windows Forms Form class, you can just use
Me.Handle. If it's being called from somewhere else, just replace 'Me' with a reference to the window, or replace that entire argument with your own code for getting the handle of the window - the handle is the unique identifier of a window which in this case is used to tell Windows what window you want to change the DWM attributes of.):
DwmSetWindowAttribute(New System.Windows.Interop.WindowInteropHelper(Me).Handle, 20, True, Runtime.InteropServices.Marshal.SizeOf(True))
To change the title bar back to light mode, simply call the same code by replace 'True' with 'False.
Tip: If calling this in a WPF Window's constructor, consider instead using the SourceInitialized or Loaded event, or using WindowInteropHelper.EnsureHandle() to ensure the window has a handle, otherwise, the window will not yet have a handle and therefore, this function will not work as it needs a window handle to apply the attribute to.
For more information about the DwmSetWindowAttribute function, see:
Before:
After:
How to check if Windows is in dark mode
If you would like your window's title bar to match the Windows colour mode, one way to check if Windows is in dark mode or light mode is to read the registry value at 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme', this value will be 0 if Windows is in dark mode and 1 if it's in light mode. You can use the functions in
Microsoft.Win32.Registry to read values from the registry. Alternatively, if your app uses UWP or WinUI via
XAML Islands, you can simply check the value of
ActualTheme of any UI Element so long as the UI Element's
RequestedTheme is
Default.
To run code when the colour mode changes, you can simply handle the
Microsoft.Win32.SystemEvents.UserPreferenceChanged event - this event will be fired whenever Windows switches between light and dark mode so can check again which mode Windows is in whenever that event fires. Alternatively, if your app uses UWP or WinUI via XAML Islands, you can use the
ActualThemeChanged event of any UI Element so long as the UI Element's
RequestedTheme is
Default.
This snippet is available in Codly. Click the appropriate link below to download the snippet. If you don't have Codly, it is available here in the Microsoft Store. Testing
Note: If you have 'show accent colour on title bars and windows borders' enabled in Windows Settings, you may not be able to see a difference between the title bar and border being in light and dark mode.
If you found this article useful or if you have any questions, let us know in the comments! Also, if this guide did help you and you use an AdBlocker and you want to support us, consider disabling it for this site - if you do, it will be greatly appreciated!
Follow us to be notified when we release new guides:
Thanks !
ReplyDeleteNo worries! Glad we could help!
DeleteThank you for the example! I have my simple app working now. But I get TWO WM_SETTINGCHANGE messages when I change user preference colors. I want to look at the LParam string value that WndProc receives, but I cannot figure out how to display in C# the original string (e.g., "Environment") the caller used. I tried copying the value into a Win32 COPYSTRUCT but got a compilation error. Do you know how to dereference the LParam in C# so that I can see the string? Thank you.
ReplyDeleteHi,
DeleteNo worries!
Windows does seem to send multiple WM_SETTINGCHANGE messages in response to colour mode changes.
As for how to access the LParam, are you using WPF or Windows Forms?
Thank you for the excellent tip. It works fine. How to draw the controls in dark mode both for winforms and WPF? Especially the scrollbars.
ReplyDeleteUnfortunately it's easier said than done, as neither Windows Forms nor WPF have dark mode support. However, Microsoft has expressed interest in adding dark mode support to these platforms and has confirmed plans to add dark mode support to WPF. You can read our article here for more info about this for WPF: https://github.com/dotnet/winforms/issues/7641#issue-1347141252. And have a look here for Windows Forms: https://github.com/dotnet/winforms/issues/6270#issue-1071089924 and here: https://github.com/dotnet/winforms/issues/7641#issue-1347141252.
DeleteIn the meantime, you will have to change the colours of your controls/create custom themes for your controls or use 3rd party libraries in order to support dark mode in these platforms.
Great example, thank you!
ReplyDeleteIs there a way to switch the title bar between dark/light mode dynamically? I have created an app that supports a light and a dark theme, but the title bar does not render immediately after calling DwmSetWindowAttribute. The change comes into effect once the I unfocus the window, but I could not trace it back to a specific event that causes the new render.
My app is running on Win10 though, maybe that has to do with it?