我正在try 制作一个具有黑暗模式能力的C#WinForm应用程序.

Windows already have 'dark themed controls' like this:
NOTE: This is made with AutoHotkey
(NOTE: This is made with AutoHotkey)
And I also want to use this.

我在一些库中搜索了以下内容:

  • DarkUI
    This uses outdated WinForm controls which misses few properties that should be available.
    It is rather 'Gray UI' but it is far better than white controls.
    • AltUI
      Fork of DarkUI, but it tried to mimic Windows' Dark Theme as similar as posible.
      But I have to manually compile the library before using it because it hasn't been on NuGet yet.
  • DarkNet
    It only handles Title Bar.
    I want to have 'dark themed controls' too.

我知道对于深色主题的控件来说,并没有什么"神奇的方法".

但是,当我在代码中搜索"黑色主题控件"时,我发现了working个AutoHotkey v2代码.

; Source: https://www.autohotkey.com/boards/viewtopic.php?t=115952
DarkColors := Map("Background", "0x202020", "Controls", "0x404040", "Font", "0xE0E0E0")
TextBGBrush := DllCall("gdi32\CreateSolidBrush", "UInt", DarkColors["Background"], "Ptr")
SetMenuAttr() {
  global DarkColors
  if (VerCompare(A_OSVersion, "10.0.17763") >= 0) {
    DWMWA_USE_IMMERSIVE_DARK_MODE := 19
    if (VerCompare(A_OSVersion, "10.0.18985") >= 0) {
      DWMWA_USE_IMMERSIVE_DARK_MODE := 20
    }
    uxtheme := DllCall("kernel32\GetModuleHandle", "Str", "uxtheme", "Ptr")
    SetPreferredAppMode := DllCall("kernel32\GetProcAddress", "Ptr", uxtheme, "Ptr", 135, "Ptr")
    FlushMenuThemes := DllCall("kernel32\GetProcAddress", "Ptr", uxtheme, "Ptr", 136, "Ptr")

    DllCall("dwmapi\DwmSetWindowAttribute", "Ptr", A_ScriptHwnd, "Int", DWMWA_USE_IMMERSIVE_DARK_MODE, "Int*", True, "Int", 4)
    DllCall(SetPreferredAppMode, "Int", 2) ; 0=Default, 1=AllowDark, 2=ForceDark, 3=ForceLight, 4=Max
    DllCall(FlushMenuThemes)
  }
}
SetWinAttr(GuiObj) {
  global DarkColors
  if (VerCompare(A_OSVersion, "10.0.17763") >= 0) {
    DWMWA_USE_IMMERSIVE_DARK_MODE := 19
    if (VerCompare(A_OSVersion, "10.0.18985") >= 0) {
      DWMWA_USE_IMMERSIVE_DARK_MODE := 20
    }
    uxtheme := DllCall("kernel32\GetModuleHandle", "Str", "uxtheme", "Ptr")
    SetPreferredAppMode := DllCall("kernel32\GetProcAddress", "Ptr", uxtheme, "Ptr", 135, "Ptr")
    FlushMenuThemes := DllCall("kernel32\GetProcAddress", "Ptr", uxtheme, "Ptr", 136, "Ptr")

    DllCall("dwmapi\DwmSetWindowAttribute", "Ptr", GuiObj.Hwnd, "Int", DWMWA_USE_IMMERSIVE_DARK_MODE, "Int*", True, "Int", 4)
    DllCall(SetPreferredAppMode, "Int", 2) ; 0=Default, 1=AllowDark, 2=ForceDark, 3=ForceLight, 4=Max
    DllCall(FlushMenuThemes)
    GuiObj.BackColor := DarkColors["Background"]
  }
}
SetWinTheme(GuiObj) {
  static GWL_WNDPROC := -4
  static GWL_STYLE := -16
  static ES_MULTILINE := 0x0004
  static LVM_GETTEXTCOLOR := 0x1023
  static LVM_SETTEXTCOLOR := 0x1024
  static LVM_GETTEXTBKCOLOR := 0x1025
  static LVM_SETTEXTBKCOLOR := 0x1026
  static LVM_GETBKCOLOR := 0x1000
  static LVM_SETBKCOLOR := 0x1001
  static LVM_GETHEADER := 0x101F
  static GetWindowLong := A_PtrSize = 8 ? "GetWindowLongPtr" : "GetWindowLong"
  static SetWindowLong := A_PtrSize = 8 ? "SetWindowLongPtr" : "SetWindowLong"
  Init := False
  LV_Init := False

  Mode_Explorer := "DarkMode_Explorer"
  Mode_CFD := "DarkMode_CFD"
  Mode_ItemsView := "DarkMode_ItemsView"

  for hWnd, GuiCtrlObj in GuiObj {
    switch GuiCtrlObj.Type {
      case "Button", "CheckBox", "ListBox", "UpDown", "Text":
      {
        DllCall("uxtheme\SetWindowTheme", "Ptr", GuiCtrlObj.hWnd, "Str", Mode_Explorer, "Ptr", 0)
      }
      case "ComboBox", "DDL":
      {
        DllCall("uxtheme\SetWindowTheme", "Ptr", GuiCtrlObj.hWnd, "Str", Mode_CFD, "Ptr", 0)
      }
      case "Edit":
      {
        if (DllCall("user32\" . GetWindowLong, "Ptr", GuiCtrlObj.hWnd, "Int", GWL_STYLE) & ES_MULTILINE) {
          DllCall("uxtheme\SetWindowTheme", "Ptr", GuiCtrlObj.hWnd, "Str", Mode_Explorer, "Ptr", 0)
        } else {
          DllCall("uxtheme\SetWindowTheme", "Ptr", GuiCtrlObj.hWnd, "Str", Mode_CFD, "Ptr", 0)
        }
      }
      case "ListView":
      {
        if !(LV_Init) {
          static LV_TEXTCOLOR := SendMessage(LVM_GETTEXTCOLOR, 0, 0, GuiCtrlObj.hWnd)
          static LV_TEXTBKCOLOR := SendMessage(LVM_GETTEXTBKCOLOR, 0, 0, GuiCtrlObj.hWnd)
          static LV_BKCOLOR := SendMessage(LVM_GETBKCOLOR, 0, 0, GuiCtrlObj.hWnd)
          LV_Init := True
        }
        GuiCtrlObj.Opt("-Redraw")
        SendMessage(LVM_SETTEXTCOLOR, 0, DarkColors["Font"], GuiCtrlObj.hWnd)
        SendMessage(LVM_SETTEXTBKCOLOR, 0, DarkColors["Background"], GuiCtrlObj.hWnd)
        SendMessage(LVM_SETBKCOLOR, 0, DarkColors["Background"], GuiCtrlObj.hWnd)
        DllCall("uxtheme\SetWindowTheme", "Ptr", GuiCtrlObj.hWnd, "Str", Mode_Explorer, "Ptr", 0)
        ; To color the selection - scrollbar turns back to normal
        ;DllCall("uxtheme\SetWindowTheme", "Ptr", GuiCtrlObj.hWnd, "Str", Mode_ItemsView, "Ptr", 0)
        LV_Header := SendMessage(LVM_GETHEADER, 0, 0, GuiCtrlObj.hWnd)
        DllCall("uxtheme\SetWindowTheme", "Ptr", LV_Header, "Str", Mode_ItemsView, "Ptr", 0)
        GuiCtrlObj.Opt("+Redraw")
      }
    }
  }
  if !(Init) {
    ; https://www.autohotkey.com/docs/v2/lib/CallbackCreate.htm#ExSubclassGUI
    global WindowProcNew := CallbackCreate(WindowProc)  ; Avoid fast-mode for subclassing.
    global WindowProcOld := DllCall("user32\" . SetWindowLong, "Ptr", GuiObj.Hwnd, "Int", GWL_WNDPROC, "Ptr", WindowProcNew, "Ptr")
    Init := True
  }
}
WindowProc(hwnd, uMsg, wParam, lParam) {
  critical
  static WM_CTLCOLOREDIT := 0x0133
  static WM_CTLCOLORLISTBOX := 0x0134
  static WM_CTLCOLORBTN := 0x0135
  static WM_CTLCOLORSTATIC := 0x0138
  static DC_BRUSH := 18

  switch uMsg {
    case WM_CTLCOLOREDIT, WM_CTLCOLORLISTBOX:
    {
      DllCall("gdi32\SetTextColor", "Ptr", wParam, "UInt", DarkColors["Font"])
      DllCall("gdi32\SetBkColor", "Ptr", wParam, "UInt", DarkColors["Controls"])
      DllCall("gdi32\SetDCBrushColor", "Ptr", wParam, "UInt", DarkColors["Controls"], "UInt")
      return DllCall("gdi32\GetStockObject", "Int", DC_BRUSH, "Ptr")
    }
    case WM_CTLCOLORBTN:
    {
      DllCall("gdi32\SetDCBrushColor", "Ptr", wParam, "UInt", DarkColors["Background"], "UInt")
      return DllCall("gdi32\GetStockObject", "Int", DC_BRUSH, "Ptr")
    }
    case WM_CTLCOLORSTATIC:
    {
      DllCall("gdi32\SetTextColor", "Ptr", wParam, "UInt", DarkColors["Font"])
      DllCall("gdi32\SetBkColor", "Ptr", wParam, "UInt", DarkColors["Background"])
      return TextBGBrush
    }
  }
  return DllCall("user32\CallWindowProc", "Ptr", WindowProcOld, "Ptr", hwnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam)
}

我还发现了this approach个似乎使用类似的方式(每个控件)来为控件设置黑色主题的控件.

有没有其他我错过的"黑暗主题控件"库?或者,有没有更好的方法来设置控件的黑色主题?

PS.
I personally don't want to use WPF even though it has nice 'dark theme' because I couldn't figure out how to place controls freely like in WinForm.

推荐答案

似乎你已经投入了一些精力来探索在你的C#WinForm应用程序中实现黑色主题的各种 Select .提供的AutoHotkey v2代码演示了一种使用Windows API调用实现控件暗模式的方法.

如果您正在寻找其他库或方法,可以考虑使用MetroFrame库,它提供了Metro风格的用户界面,包括黑色主题.你可以在NuGet上找到它,它与WinForms兼容.

要使用MetroFramework,您可以使用NuGet Package Manager Console安装它:

Install-Package MetroFramework

然后,您可以通过设置Style属性将黑色主题应用于您的表单:

this.Style = MetroFramework.MetroColorStyle.Black;

这个库可能提供了一种更方便的方式来实现WinForm控件的黑色主题,而不必手动处理每个控件.

Csharp相关问答推荐

使用其可能实现的基类和接口的属性的方法

在发布表单时绑定包含附加(嵌套)列表的对象列表的正确语法是什么

在一个模拟上设置一个方法,该模拟具有一个参数,该参数是一个numc函数表达式

自动映射程序在GroupBy之后使用项目

HttpConext.Request.Path和HttpConext.GetEndpoint()之间的差异

如何在Windows 11任务调度程序中每1分钟重复一次任务?

如何从ASP.NET核心MVC视图和Blazor传递数据

由于POST中的应用程序/JWT,出现不支持的内容类型异常

Cosmos SDK和Newtonsoft对静态只读记录的可能Mutations

我如何让我的秒表保持运行场景而不重置

使用ExtractIconEx(或其他方式)提取最大的可用图标

正在try 从Blazor中的API读取JSON

源代码生成器项目使用`dotnet build`编译,而不是在Visual Studio中编译?

当try 测试具有协变返回类型的抽象属性时,类似功能引发System.ArgumentException

获取混淆&Quot;模糊引用&Quot;错误

岛屿和框架中的自定义控件库.Navigate-AccessViolationException

在构造函数中传递C#函数以用作EventHandler委托的订阅服务器

从HTML元素获取 colored颜色

在Visual Studio 2022中查找Xamarin模板时遇到问题

避免在特定区域中设置Visual Studio代码的自动格式