Windows 10 上的 WinForms 深色标题栏

Jon*_*ohl 6 c# winforms

我有一个 WinForms 应用程序,它会自动调整到 Windows 10 上的暗/亮主题。我的问题是我的窗口的标题栏始终保持白色,无论用户选择哪个主题。

在此处输入图片说明
顶部是最新的,底部是我想要的(用 Photoshop 模拟)

参见explorer示例。这不是 UWP 应用程序,但它在 Windows 1903 和更高版本上使用深色标题栏(选择深色主题时)。

我怎样才能达到同样的目的?我不想使用任何自定义标题栏,因为我希望应用程序的外观和行为也像旧 Windows 版本上的任何本机应用程序一样。

Jon*_*ohl 12

所以经过长时间的搜索,我终于找到了答案。诀窍是使用dwmapi.dll'sDwmSetWindowAttribute并将未记录的常量传递DWMWA_USE_IMMERSIVE_DARK_MODE给函数。在 C# 中,这个代码看起来有点像这样(适用于 WinForms 和 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 太棒了,它有效!提示:在表单中的“InitializeComponent();”调用之后添加“UseImmersiveDarkMode(this.Handle, true);”。 (5认同)
  • 该常量不再是未记录的,因为我刚刚为其打开了此 PR:https://github.com/MicrosoftDocs/sdk-api/pull/966/files。另外,为了强制窗口重新绘制,可以在整个窗体上调用 Invalidate() 或使用 WM_NCPAINT 调用 SendMessageW 来强制再次发生 NCPAINT。 (4认同)
  • 就我而言,我必须另外隐藏/显示窗口才能使此更改生效。 (2认同)
  • 很高兴知道需要什么其他调用来强制更改它。但是,在不闪烁窗口或改变焦点的情况下实现此目的的一种方法是相对于当前不透明度稍微改变窗口不透明度 +/- 一小部分,例如 0.0000000001,然后将其返回。这将进行所需的调用,强制其重新绘制。 (2认同)
  • @PkKingX11查看[参考源](https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,2895b1373643ef53)后,似乎对SetWindowPos的调用就像似乎需要以下内容: `SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);` 虽然我还没有尝试过 (2认同)

小智 10

最快的方法:

[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);
}
Run Code Online (Sandbox Code Playgroud)

  • 您可能想要提供更多细节这些值的确切含义,或者更好地使用将文字映射到对象标识符的函数。 (3认同)