Reputation: 1142
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.
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.
Upvotes: 14
Views: 15945
Reputation: 21
.NET 9 now has Application.SetColorMode. Where SystemColorMode.Dark sets a "Dark Mode". But apparently only works on Windows 11.
Upvotes: 0
Reputation: 1
Just a note about useage.
I placed the call to use dark mode method in a form's constructor and found that forms inheriting from this form were having problems with StartPosition (possibly the base form, but that's untested).
Changing an inheriting form's StartPosition in the designer to any other value, caused the Location to be used instead (Location was being added to inheriting form's designer code - manually deleting it worked, but it's not a good solution.) Reversing the change does not undo the problem.
Solution is to place the dark mode call in the Load event (tested only on .Net 6)
Upvotes: 0
Reputation: 142
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(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
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(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
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
}
Upvotes: 3
Reputation: 141
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);
}
Upvotes: 14
Reputation: 1142
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;
}
Upvotes: 40