我需要将应用程序的所有非客户区域(边框、标题、菜单栏)设置为特定颜色。
我可以使用 DwmSetWindowAttribute 设置标题和边框。但菜单栏让我无法理解。
我尝试创建画笔并使用 SetMenuInfo,如下所示:
LOGBRUSH lbr;
memset(&lbr, 0, sizeof(LOGBRUSH));
lbr.lbColor = RGB(200,0,0);
HBRUSH GBRUSH = CreateBrushIndirect(&lbr);
// Apply Brush to the Menu
MENUINFO mi = { 0 };
memset(&mi, 0, sizeof(MENUINFO));
mi.cbSize = sizeof(mi);
GetMenuInfo(GetMenu(), &mi);
mi.fMask = MIM_BACKGROUND | MIM_APPLYTOSUBMENUS;
mi.hbrBack = GBRUSH;
BOOL bRet = SetMenuInfo(GetMenu(), &mi);
...但这仅影响弹出子菜单,而不影响菜单栏本身。
我找到了使用 SetWindowTheme 禁用主题的建议;这有效,并允许我设置菜单栏颜色,但我无法再设置标题和边框。
如果无法在整个菜单上进行完全的所有者绘制,有没有办法可以使用这些(或其他)API 来设置所有非客户端区域?
0x0091
WM_UAHDRAWMENU
发送此消息是为了绘制菜单栏
背景。 LPARAM
是 UAHMENU 指针。
将背景绘制到hdc中。菜单栏矩形可以通过以下方式计算 使用
GetMenuBarInfo
获取 rcBar,使用 GetWindowRect
获取
窗口矩形,然后OffsetRect(rcBar, -rcWindow.left, -rcWindow.top).
0x0092
WM_UAHDRAWMENUITEM
发送此消息是为了绘制一个
单独的菜单项。 LPARAM
是一个 UAHDRAWMENUITEM
指针。
菜单项的从零开始的位置位于
umi.iPosition
。画
使用中的矩形将背景和文本放入um.hdc
dis.rcItem
。菜单项的状态为dis.itemState
,
通常是任意 (ODS_DEFAULT | ODS_INACTIVE | ODS_HOTLIGHT | ODS_SELECTED | ODS_GRAYED | ODS_DISABLED | ODS_NOACCEL)
的组合
0x0094
WM_UAHMEASUREMENUITEM
发送此消息是为了测量
单独的菜单项。 LPARAM 是一个 UAHMEASUREMENUITEM
指针。
将此转发到
DefWindowProc
将填写以下值
预期的,如果您只是更改背景,这就是您所需要的
颜色或等等,不要弄乱宽度。 MEASUREITEMSTRUCT
旨在填充文本的大小。
这是一个基于VS Windows桌面应用程序的示例。 这是menubarsample.cpp:
#include "framework.h"
#include "resource.h"
#include "UAHMenuBar.h"
#include <Uxtheme.h>
#include <vsstyle.h>
#pragma comment(lib, "uxtheme.lib")
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_MENUBARSAMPLE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MENUBARSAMPLE));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MENUBARSAMPLE));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MENUBARSAMPLE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
static HTHEME g_menuTheme = nullptr;
// ugly colors for illustration purposes
static HBRUSH g_brBarBackground = CreateSolidBrush(RGB(200, 0, 0));
void UAHDrawMenuNCBottomLine(HWND hWnd)
{
MENUBARINFO mbi = { sizeof(mbi) };
if (!GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi))
{
return;
}
RECT rcClient = { 0 };
GetClientRect(hWnd, &rcClient);
MapWindowPoints(hWnd, nullptr, (POINT*)&rcClient, 2);
RECT rcWindow = { 0 };
GetWindowRect(hWnd, &rcWindow);
OffsetRect(&rcClient, -rcWindow.left, -rcWindow.top);
// the rcBar is offset by the window rect
RECT rcAnnoyingLine = rcClient;
rcAnnoyingLine.bottom = rcAnnoyingLine.top;
rcAnnoyingLine.top--;
HDC hdc = GetWindowDC(hWnd);
FillRect(hdc, &rcAnnoyingLine, g_brBarBackground);
ReleaseDC(hWnd, hdc);
}
bool UAHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr)
{
switch (message)
{
case WM_UAHDRAWMENU:
{
UAHMENU* pUDM = (UAHMENU*)lParam;
RECT rc = { 0 };
// get the menubar rect
{
MENUBARINFO mbi = { sizeof(mbi) };
GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// the rcBar is offset by the window rect
rc = mbi.rcBar;
OffsetRect(&rc, -rcWindow.left, -rcWindow.top);
}
FillRect(pUDM->hdc, &rc, g_brBarBackground);
return true;
}
case WM_UAHDRAWMENUITEM:
{
UAHDRAWMENUITEM* pUDMI = (UAHDRAWMENUITEM*)lParam;
// ugly colors for illustration purposes
static HBRUSH g_brItemBackground = CreateSolidBrush(RGB(0, 200, 0));
static HBRUSH g_brItemBackgroundHot = CreateSolidBrush(RGB(0, 0, 200));
static HBRUSH g_brItemBackgroundSelected = CreateSolidBrush(RGB(100, 100, 0));
HBRUSH* pbrBackground = &g_brItemBackground;
// get the menu item string
wchar_t menuString[256] = { 0 };
MENUITEMINFO mii = { sizeof(mii), MIIM_STRING };
{
mii.dwTypeData = menuString;
mii.cch = (sizeof(menuString) / 2) - 1;
GetMenuItemInfo(pUDMI->um.hmenu, pUDMI->umi.iPosition, TRUE, &mii);
}
// get the item state for drawing
DWORD dwFlags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
int iTextStateID = 0;
int iBackgroundStateID = 0;
{
if ((pUDMI->dis.itemState & ODS_INACTIVE) | (pUDMI->dis.itemState & ODS_DEFAULT)) {
// normal display
iTextStateID = MPI_NORMAL;
iBackgroundStateID = MPI_NORMAL;
}
if (pUDMI->dis.itemState & ODS_HOTLIGHT) {
// hot tracking
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
pbrBackground = &g_brItemBackgroundHot;
}
if (pUDMI->dis.itemState & ODS_SELECTED) {
// clicked -- MENU_POPUPITEM has no state for this, though MENU_BARITEM does
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
pbrBackground = &g_brItemBackgroundSelected;
}
if ((pUDMI->dis.itemState & ODS_GRAYED) || (pUDMI->dis.itemState & ODS_DISABLED)) {
// disabled / grey text
iTextStateID = MPI_DISABLED;
iBackgroundStateID = MPI_DISABLED;
}
if (pUDMI->dis.itemState & ODS_NOACCEL) {
dwFlags |= DT_HIDEPREFIX;
}
}
if (!g_menuTheme) {
g_menuTheme = OpenThemeData(hWnd, L"Menu");
}
DTTOPTS opts = { sizeof(opts), DTT_TEXTCOLOR, iTextStateID != MPI_DISABLED ? RGB(0x00, 0x00, 0x20) : RGB(0x40, 0x40, 0x40) };
FillRect(pUDMI->um.hdc, &pUDMI->dis.rcItem, *pbrBackground);
DrawThemeTextEx(g_menuTheme, pUDMI->um.hdc, MENU_BARITEM, MBI_NORMAL, menuString, mii.cch, dwFlags, &pUDMI->dis.rcItem, &opts);
return true;
}
case WM_UAHMEASUREMENUITEM:
{
UAHMEASUREMENUITEM* pMmi = (UAHMEASUREMENUITEM*)lParam;
// allow the default window procedure to handle the message
// since we don't really care about changing the width
*lr = DefWindowProc(hWnd, message, wParam, lParam);
// but we can modify it here to make it 1/3rd wider for example
pMmi->mis.itemWidth = (pMmi->mis.itemWidth * 4) / 3;
return true;
}
case WM_THEMECHANGED:
{
if (g_menuTheme) {
CloseThemeData(g_menuTheme);
g_menuTheme = nullptr;
}
// continue processing in main wndproc
return false;
}
case WM_NCPAINT:
case WM_NCACTIVATE:
*lr = DefWindowProc(hWnd, message, wParam, lParam);
UAHDrawMenuNCBottomLine(hWnd);
return true;
break;
default:
return false;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT lr = 0;
if (UAHWndProc(hWnd, message, wParam, lParam, &lr)) {
return lr;
}
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
这是“UAHMenuBar.h”:
#pragma once
#include<windows.h>
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool UAHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr);
// window messages related to menu bar drawing
#define WM_UAHDESTROYWINDOW 0x0090 // handled by DefWindowProc
#define WM_UAHDRAWMENU 0x0091 // lParam is UAHMENU
#define WM_UAHDRAWMENUITEM 0x0092 // lParam is UAHDRAWMENUITEM
#define WM_UAHINITMENU 0x0093 // handled by DefWindowProc
#define WM_UAHMEASUREMENUITEM 0x0094 // lParam is UAHMEASUREMENUITEM
#define WM_UAHNCPAINTMENUPOPUP 0x0095 // handled by DefWindowProc
// describes the sizes of the menu bar or menu item
typedef union tagUAHMENUITEMMETRICS
{
// cx appears to be 14 / 0xE less than rcItem's width!
// cy 0x14 seems stable, i wonder if it is 4 less than rcItem's height which is always 24 atm
struct {
DWORD cx;
DWORD cy;
} rgsizeBar[2];
struct {
DWORD cx;
DWORD cy;
} rgsizePopup[4];
} UAHMENUITEMMETRICS;
// not really used in our case but part of the other structures
typedef struct tagUAHMENUPOPUPMETRICS
{
DWORD rgcx[4];
DWORD fUpdateMaxWidths : 2; // from kernel symbols, padded to full dword
} UAHMENUPOPUPMETRICS;
// hmenu is the main window menu; hdc is the context to draw in
typedef struct tagUAHMENU
{
HMENU hmenu;
HDC hdc;
DWORD dwFlags; // no idea what these mean, in my testing it's either 0x00000a00 or sometimes 0x00000a10
} UAHMENU;
// menu items are always referred to by iPosition here
typedef struct tagUAHMENUITEM
{
int iPosition; // 0-based position of menu item in menubar
UAHMENUITEMMETRICS umim;
UAHMENUPOPUPMETRICS umpm;
} UAHMENUITEM;
// the DRAWITEMSTRUCT contains the states of the menu items, as well as
// the position index of the item in the menu, which is duplicated in
// the UAHMENUITEM's iPosition as well
typedef struct UAHDRAWMENUITEM
{
DRAWITEMSTRUCT dis; // itemID looks uninitialized
UAHMENU um;
UAHMENUITEM umi;
} UAHDRAWMENUITEM;
// the MEASUREITEMSTRUCT is intended to be filled with the size of the item
// height appears to be ignored, but width can be modified
typedef struct tagUAHMEASUREMENUITEM
{
MEASUREITEMSTRUCT mis;
UAHMENU um;
UAHMENUITEM umi;
} UAHMEASUREMENUITEM;
#pragma once