我正在尝试创建一个 Win32 应用程序(没有其他 GUI 依赖项),它有一组选项卡和这些选项卡中的控件。我按照 Raymond Chen 的指南 创建了以下合理的最小示例,这似乎与 Microsoft 的文档 相矛盾,但无论哪种方式似乎都不会影响绘图:
#include <Windows.h>
#include <windowsx.h>
#include <CommCtrl.h>
#include <uxtheme.h>
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW));
EndPaint(hwnd, &ps);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int main(int argc, char **argv) {
HINSTANCE hinst = GetModuleHandle(NULL);
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
LOGFONT lf;
GetObject (GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &lf);
HFONT hfont = CreateFont (lf.lfHeight, lf.lfWidth,
lf.lfEscapement, lf.lfOrientation, lf.lfWeight,
lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet,
lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,
lf.lfPitchAndFamily, lf.lfFaceName);
const wchar_t CLASS_NAME[] = L"Window Class";
WNDCLASS wc = { 0,
wndproc,
0,
0,
hinst,
NULL,
NULL,
NULL,
NULL,
(LPCSTR)CLASS_NAME
};
RegisterClass(&wc);
HWND window_hwnd = CreateWindow((LPCSTR)CLASS_NAME, "Tabs Example", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hinst, NULL);
SendMessage(window_hwnd, WM_SETFONT, (WPARAM)hfont, TRUE);
HWND tab_hwnd = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_VISIBLE, 0, 0, 640, 480, window_hwnd, NULL, hinst, NULL);
SendMessage(tab_hwnd, WM_SETFONT, (WPARAM)hfont, TRUE);
TCITEM item;
item.mask = TCIF_TEXT | TCIF_IMAGE;
item.iImage = -1;
item.pszText = "One!";
TabCtrl_InsertItem(tab_hwnd, 0, &item);
item.pszText = "Two!";
TabCtrl_InsertItem(tab_hwnd, 1, &item);
item.pszText = "Three!";
TabCtrl_InsertItem(tab_hwnd, 2, &item);
HWND groupbox_hwnd = CreateWindow(WC_BUTTON, "Groupbox", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_GROUPBOX, 10, 32, 600, 400, window_hwnd, NULL, hinst, NULL);
SendMessage(groupbox_hwnd, WM_SETFONT, (WPARAM)hfont, TRUE);
HWND text_hwnd = CreateWindow(WC_STATIC, "Static Text", WS_CHILD | WS_VISIBLE | SS_CENTER, 100, 100, 100, 100, window_hwnd, NULL, hinst, NULL);
SendMessage(text_hwnd, WM_SETFONT, (WPARAM)hfont, TRUE);
ShowWindow(window_hwnd, SW_NORMAL);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
问题是,运行时,标签内容绘制不正确。选项卡中的每个控件都有一个带有窗口背景颜色的背景:
(注意“Groupbox”文本和“静态文本”背后不正确的背景)
我试过使用像这样的
WM_CTLCOLORSTATIC
使用透明背景:
case WM_CTLCOLORSTATIC: {
HDC hEdit = (HDC)wParam;
SetBkMode(hEdit, TRANSPARENT);
SetTextColor(hEdit, RGB(0, 0, 0));
return GetStockObject(HOLLOW_BRUSH);
}
但这会导致组框渲染不正确:
(注意在“Groupbox”文本后面绘制的 groupbox 分隔线)
此外,我尝试将选项卡控件重新排序到底部,这使得 Groupbox 显示不正确:
SetWindowPos(tab_hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
(注意整个组框下的灰色背景)
对于大多数控件
WM_PRINTCLIENT
都可以。
case WM_PRINTCLIENT:
{
// Paint area outside tabs control here if needed.
...
// Now paint tabs area
RECT r1, r2;
GetWindowRect(hwnd, &r1);
GetWindowRect(tags_hwnd, &r2);
POINT old;
::SetWindowOrgEx((HDC)wparam, r1.left - r2.left, r1.top - r2.top, &old);
LRESULT result = SendMessage(tabs_hwnd, WM_PRINTCLIENT, wparam, lparam);
::SetWindowOrgEx((HDC)wparam, old.x, old.y, 0);
return result;
}
WM_PRINTCLIENT
应该绘制整个客户区域,因此经常使用子对话框或帮助窗口是有原因的。
不幸的是STATIC控制不调用
WM_PRINTWINDOW
,可以使用WM_CTLCOLORSTATIC
代替。
这个版本在
WM_CTLCOLORSTATIC
中作画有点难看。
更正确的解决方案是创建新的 hdc 和内存位图,将背景渲染到其中,从中创建画笔
并返回结果。
case WM_CTLCOLORSTATIC:
{
// Let tabs set initial state
LRESULT result = SendMessage(tabs_hwnd, WM_CTLCOLORSTATIC, wparam, lparam);
if ( IsAppThemed() )
{
// A little ugly, we paint here.
// The alternative is to create brush for needed area.
RECT r1, r2;
GetWindowRect(hwnd, &r1);
GetWindowRect(tags_hwnd, &r2);
POINT old;
::SetWindowOrgEx((HDC)wparam, r1.left - r2.left, r1.top - r2.top, &old);
LRESULT result = SendMessage(tabs_hwnd, WM_PRINTCLIENT, wparam, lparam);
::SetWindowOrgEx((HDC)wparam, old.x, old.y, 0);
SetBkMode((HDC)wparam, TRANSPARENT);
result = GetStockObject(HOLLOW_BRUSH);
}
return result;
}