How to enable dark title bar in WPF and Windows Forms/WinForms apps in C# and VB

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:

WPF app with dark title bar

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:

WPF app with light title bar

After:

WPF app with dark title bar


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.
You can use our app Window Commander to check and change the value of DWMWA_USE_IMMERSIVE_DARK_MODE, making it easier to experiment with this attribute. Window Commander is available here: https://www.microsoft.com/store/productId/9P8316P3V63P.

Window Commander window properties window DWM tab

Our app AutoColourMode might also come in useful here as it allows you to quickly switch between light and dark mode from the taskbar. AutoColourMode is available here: https://www.microsoft.com/store/productId/9PL4MS6C2VPC.

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:

Comments

  1. 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
    Replies
    1. Hi,
      No 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?

      Delete
  2. 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.

    ReplyDelete
    Replies
    1. Unfortunately 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.

      In 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.

      Delete
  3. Great example, thank you!
    Is 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?

    ReplyDelete
    Replies
    1. No worries! Unfortunately we have the same issue on Windows 10 in our testing but not Windows 11, so it may be an issue with Windows 10 that Microsoft has to fix. It is possible though, that there may be a way to force the title bar to update. Additionally, when setting the title bar colour when initially showing the window, you can try setting it before the window is shown to fix this issue.

      Delete

Post a Comment

Popular posts from this blog

How to show placeholder text in a WPF TextBox in C# and VB

How to use modern icons in XAML in WPF on Windows 10 and 11

How to change the colour of a WPF or Windows Forms/WinForms title bar in Windows 11 in C# and VB

Microsoft WebView2: How to check if the WebView2 runtime is installed in C# and VB