[Win32] Form dark mode, how to use C++, WinForm, WPF, judge color mode, respond to color change message, set title bar dark color.

It's 2023, if the program we write doesn't support the dark mode, then it's not justified.

Determine whether it is dark mode

In Windows, to determine whether the color mode of the current system is dark, it can be realized by querying the registry key.

Here is the sample code in C++:

HKEY hKey;
const wchar_t* subKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
const wchar_t* valueName = L"AppsUseLightTheme";
DWORD value = -1;
DWORD valueSize = sizeof(DWORD);
if (RegOpenKeyEx(HKEY_CURRENT_USER, subKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
    
    
    HRESULT hr = RegGetValue(hKey, nullptr, valueName, RRF_RT_REG_DWORD, nullptr, &value, &valueSize);
    if (hr != S_OK)
    {
    
    
        value = -1;    // 不要假定该键必须存在,如果找不到将返回默认值
    }
    RegCloseKey(hKey);
}

if (value == 0)
{
    
    
    // 当前使用暗色主题
}
else if (value == 1)
{
    
    
    // 当前使用亮色主题
}
else
{
    
    
    // 无法确定当前主题
}

Here is the C# sample code:

using Microsoft.Win32;

RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize");
if (key != null)
{
    
    
    int appsUseLightTheme = (int)key.GetValue("AppsUseLightTheme", -1);
    if (appsUseLightTheme == 0)
    {
    
    
        // 当前使用暗色主题
    }
    else if (appsUseLightTheme == 1)
    {
    
    
        // 当前使用亮色主题
    }
    else
    {
    
    
        // 无法确定当前主题
    }
    key.Close();
}

Set window dark mode

Windows comes with a dark mode WinAPI, which can be called directly to turn the window title bar into a dark color.

Here is the sample code in C++:

#include <windows.h>
#include <dwmapi.h>

int main() {
    
    
	HWND hWnd = GetMainWindowHandle(); // 获取主窗口句柄
	DWORD dwAttribute = DWMWA_USE_IMMERSIVE_DARK_MODE; // 设置暗色模式属性
	BOOL bValue = TRUE; // 启用暗色模式
	DwmSetWindowAttribute(hWnd, dwAttribute, &bValue, sizeof(bValue)); // 设置窗口属性

	return 0;
}

Here is the sample code in C#:

[DllImport("dwmapi.dll", PreserveSig = true)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DwmWindowAttribute attr, ref int attrValue, int attrSize);

public static bool EnableDarkModeForWindow(IntPtr hWnd, bool enable)
{
    
    
	int darkMode = enable ? 1 : 0;
	int hr = DwmSetWindowAttribute(hWnd, DwmWindowAttribute.UseImmersiveDarkMode, ref darkMode, sizeof(int));
	return hr >= 0;
}

public enum DwmWindowAttribute : uint
{
    
    
	NCRenderingEnabled = 1,
	NCRenderingPolicy,
	TransitionsForceDisabled,
	AllowNCPaint,
	CaptionButtonBounds,
	NonClientRtlLayout,
	ForceIconicRepresentation,
	Flip3DPolicy,
	ExtendedFrameBounds,
	HasIconicBitmap,
	DisallowPeek,
	ExcludedFromPeek,
	Cloak,
	Cloaked,
	FreezeRepresentation,
	PassiveUpdateMode,
	UseHostBackdropBrush,
	UseImmersiveDarkMode = 20,
	WindowCornerPreference = 33,
	BorderColor,
	CaptionColor,
	TextColor,
	VisibleFrameBorderThickness,
	SystemBackdropType,
	Last
}

Respond to color change

When the system color changes, a THEMECHANGEDmessage is sent to all windows:

In C++, you can capture this message in WndProc, and then perform specific operations. For example, as mentioned above, set the window dark mode.

#include <Windows.h>
#include <iostream>

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int main() {
    
    
    // 注册窗口类
    WNDCLASS wc = {
    
    };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = L"MyWindowClass";
    if (!RegisterClass(&wc)) {
    
    
        std::cerr << "Failed to register window class" << std::endl;
        return 1;
    }

    // 创建窗口
    HWND hwnd = CreateWindow(
        L"MyWindowClass",
        L"My Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, GetModuleHandle(NULL), NULL
    );
    if (!hwnd) {
    
    
        std::cerr << "Failed to create window" << std::endl;
        return 1;
    }

    // 显示窗口
    ShowWindow(hwnd, SW_SHOWDEFAULT);

    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
    
    
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    
    
    switch (uMsg) {
    
    
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    case WM_THEMECHANGED:
        std::cout << "Theme changed" << std::endl;
        return 0;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

For C#, if you are using WinForm or WPF, you can directly use the C# encapsulated SystemEventsclass, which has an UserPreferenceChangedevent, by subscribing to this event, and judging the specific category, and finally perform the operation:

SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;

private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
    
    
    if (e.Category == UserPreferenceCategory.General)
    {
    
    
        // 更新颜色主题
    }
}

Guess you like

Origin blog.csdn.net/m0_46555380/article/details/129945529