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#
[DllImport("dwmapi.dll", PreserveSig = true)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref bool attrValue, int attrSize);
And in VB:
Public Shared Function DwmSetWindowAttribute(hwnd As IntPtr, attr As Integer, ByRef attrValue As Boolean, attrSize As Integer) As Integer
Once you've defined the function, you can call it from your code to change various DWMWINDOWATTRIBUTE
s. 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:
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
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
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.
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:
No worries! Glad we could help!Delete
Thank 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.ReplyDelete
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?