skip to Main Content

I have a WinForms application which automatically adjusts to the dark/light theme on Windows 10. My problem is that the title bar of my window always stays white, regardless which theme the user selects.

enter image description here
Top is current, bottom is how I want it (simulated with Photoshop)

See explorer for example. That is not an UWP app, however it uses a dark title bar on Windows 1903 and newer (when a dark theme is selected).

How can I achieve the same thing? I do not want to use any custom titlebar as I want the application to look and behave like any native application on older Windows versions as well.

3

Answers


  1. Chosen as BEST ANSWER

    So after some long searching, I have finally found the answer for this. The trick is to use dwmapi.dll's DwmSetWindowAttribute and passing the undocumented constant DWMWA_USE_IMMERSIVE_DARK_MODE into the function. In C#, the code for this looks a little something like this (works with both WinForms and WPF):

    /*
    using System.Runtime.InteropServices;
    */
    
    [DllImport("dwmapi.dll")]
    private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
    
    private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
    private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
    
    private static bool UseImmersiveDarkMode(IntPtr handle, bool enabled)
    {
        if (IsWindows10OrGreater(17763))
        {
            var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
            if (IsWindows10OrGreater(18985))
            {
                attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
            }
    
            int useImmersiveDarkMode = enabled ? 1 : 0;
            return DwmSetWindowAttribute(handle, (int)attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
        }
    
        return false;
    }
    
    private static bool IsWindows10OrGreater(int build = -1)
    {
        return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
    }
    

  2. The fastest way:

    [DllImport("DwmApi")] //System.Runtime.InteropServices
    private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, int[] attrValue, int attrSize);
    
    protected override void OnHandleCreated(EventArgs e)
    {
        if (DwmSetWindowAttribute(Handle, 19, new[] { 1 }, 4) != 0)
            DwmSetWindowAttribute(Handle, 20, new[] { 1 }, 4);
    }
    
    Login or Signup to reply.
  3. For the solution from Jonas Kohl, remember that for .net fw 4.8.1 and prior, the version returned is not ok, fixed in .Net6, here a snippet (.Net 5 is not managed):

        private static bool IsWindows10OrGreater(int build = -1)
        {
            return WindowsVersion() >= 10 && WindowsBuildNumber() >= build;
        }
    
        public static int WindowsVersion()
        {
         //for .Net4.8 and Minor
         #if NETFRAMEWORK
            int result = 10;
            var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWAREMicrosoftWindows NTCurrentVersion");
            string[] productName = reg.GetValue("ProductName").ToString().Split((char)32);
            int.TryParse(productName[1], out result);
            return result;
         #else
            //fixed in .Net6
            return System.Environment.OSVersion.Version.Major;
         #endif
        }
    
        public static int WindowsBuildNumber()
        {
            //for .Net4.8 and Minor
        #if NETFRAMEWORK
            int result = 22000;
            var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWAREMicrosoftWindows NTCurrentVersion");
            string buildNumber = reg.GetValue("CurrentBuildNumber").ToString();
            int.TryParse(buildNumber, out result);
            return result;
        #endif
    
        #if NET
            //fixed in .Net6
            return System.Environment.OSVersion.Version.Build;
        #endif
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search