Reputation: 2315
I found this article on Win32 Dark Mode which references this github project. I used a forked version with more up to date build numbers (and added additional ones since then).
I created a generic handler for this dialog based application:
BOOL CALLBACK SetDarkThemeForChildren(HWND hwnd, LPARAM lparam)
{
if (!::IsWindow(hwnd)) {
return TRUE;
}
TCHAR classname[80];
classname[0]=0;
::GetClassName(hwnd, classname, _countof(classname));
if (_tcsicmp(classname, _T("SysListView32"))==0) {
InitListView(hwnd);
}
SetWindowTheme(hwnd, L"Explorer", nullptr);
return TRUE;
}
//---------------------------------------------------------------------------
BOOL CALLBACK DarkThemeChangedForChildren(HWND hwnd, LPARAM lparam)
{
if (!::IsWindow(hwnd)) {
return TRUE;
}
_AllowDarkModeForWindow(hwnd, g_darkModeEnabled);
SendMessage(hwnd, WM_THEMECHANGED, 0, 0);
return TRUE;
}
//---------------------------------------------------------------------------
// Purpose: Handle messages for darkmode
//
// Input: hwnddlg - [i] dialog handle
// msg - [i] dialog message
// wparam - [i] win message parameter
// lparm - [i] win message parameter
// newbrush - [o] brush to use for message (only if true returned)
//
// Output: TRUE handled - abort normal handlers as would be appropriate
// FALSE not handled or use default handlers.
//
// Notes: Only returns TRUE for messages that were WM_CTLCOLOR* type and we
// handled it (and returned the HBRUSH) for dark mode.
//
bool HandleDarkMode(HWND hwnddlg, UINT msg, WPARAM wparam, LPARAM lparam, HBRUSH *newbrush)
{
bool result=false;
static HBRUSH hbrBkgnd = nullptr;
constexpr COLORREF darkBkColor = 0x383838;
constexpr COLORREF darkTextColor = 0xFFFFFF;
switch (msg) {
case UWM_INITCHILDREN:
case WM_INITDIALOG:
{
if (g_darkModeSupported) {
_AllowDarkModeForWindow(hwnddlg, true);
RefreshTitleBarThemeColor(hwnddlg);
EnumChildWindows(hwnddlg, SetDarkThemeForChildren, NULL);
SendMessage(hwnddlg, WM_THEMECHANGED, 0, 0);
}
}
break;
case WM_DESTROY:
// only for main dialog
if (hbrBkgnd && hwnddlg==g_hwndDlgMain) {
DeleteObject(hbrBkgnd);
hbrBkgnd = nullptr;
}
break;
case WM_CTLCOLOR:
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
{
if (g_darkModeSupported && g_darkModeEnabled) {
HDC hdc = reinterpret_cast<HDC>(wparam);
SetTextColor(hdc, darkTextColor);
SetBkColor(hdc, darkBkColor);
if (!hbrBkgnd)
hbrBkgnd = CreateSolidBrush(darkBkColor);
*newbrush=hbrBkgnd;
return true;
}
}
break;
case WM_SETTINGCHANGE:
{
if (g_darkModeSupported && IsColorSchemeChangeMessage(lparam))
PostMessage(hwnddlg, WM_THEMECHANGED, 0, 0);
}
break;
case WM_THEMECHANGED:
{
if (g_darkModeSupported) {
_AllowDarkModeForWindow(hwnddlg, g_darkModeEnabled);
RefreshTitleBarThemeColor(hwnddlg);
EnumChildWindows(hwnddlg, DarkThemeChangedForChildren, NULL);
UpdateWindow(hwnddlg);
}
}
break;
}
*newbrush=NULL;
return false;
}
Basically in the Window Procedure before the switch statement I do this:
HBRUSH newbrush;
bool darkmode=HandleDarkMode(hwnddlg, message, wparam, lparam, &newbrush);
// handle WM_CTLCOLOR* since we don't in main switch
if (darkmode) {
return (INT_PTR) newbrush;
}
It basically works for most things, but using the latest Win10 Pro x64 version I find certain things don't work:
Group Box: Text is black (everything else if fine (line is white, background is black)
Check Box/Radio Button: They still draw with black text and checkbox itself is still white (which is fine with me, the black text is the problem).
ComboBox: The combo box is still light style, while the drop-down selection is correctly dark.
4. ListView: The column header background is still white/light. The text has changed but hard to see on the white/light background. The body of the list view is fine.
Found Issue for #4: Need to skip SysHeader32
so the theme is not changed.
Any ideas on what may be happening and what is needed to fix it?
Upvotes: 1
Views: 54
Reputation: 141
The Dark theme for Win32 is mostly undocumented, but internally dark graphics in Aero.msstyles are implemented through several sub-styles. For the few controls that actually support it, that is.
You are already using SetWindowTheme
so try:
SetWindowTheme (..., L"DarkMode_Explorer", NULL); // in general
SetWindowTheme (..., L"ExplorerStatusBar", NULL); // for msctls_statusbar32
SetWindowTheme (..., L"DarkMode_CFD", NULL); // for COMBOBOX and EDIT
SetWindowTheme (..., L"DarkMode_ItemsView", NULL); // for SysHeader32 and SysListView32
But these graphics are hopelessly incomplete, and unless your use-case exactly matches Explorer's, you may find it distorted or outright better to fully owner-draw them yourself.
Also do test your app in dark High Contrast mode. The colors are already inverted.
Upvotes: 0