正如我们从这个截图中看到的,我采取了:
http://www.picpaste.com/pics/skinned_window.1386408792.png
出于一些奇怪的原因,Windows在我的蒙皮标题栏(黄色矩形)顶部的标题栏中绘制了自己的(未设置样式)最小化/最大化/关闭按钮,如红色箭头所示.它还在这些按钮的底部绘制了一条令人烦恼的1像素大小的线条,正如您从screeenshot中看到的那样.
您可以注意到我正在为窗口蒙皮:我正在绘制自己的标题栏(黄色矩形),并调整边框(青色,品红色,红色矩形)的大小.它们现在只是矩形,但我无法理解为什么Windows在我的黄色矩形上绘制,我在非客户区绘制,只需在WM_NCPAINT发生时绘制.一切都很好,除了这个奇怪的事情.
这种情况不会一直发生,它会在使用蒙皮窗口一段时间后发生,调整大小并最大化/最小化2-3次.特别是当我在小块程序的执行某一点上单击鼠标按下鼠标栏时,会发生这种情况.事实上,我认为WM_NCHITTEST消息中的问题可能有问题,但似乎并非如此.有一些错误,可能是一些Window Style或Window Extended Style标志错了.
我无法解释这一点,我正确地重新实现WM_NCPAINT消息(我猜),所以Windows不应该理解我正在绘制自己的标题栏吗?为什么要覆盖我的图纸?!这是Windows XP的错误吗?它似乎不会发生在Windows 7中,但我不太确定.
也许我只是错过了为此重新实现WM_*消息.有人可以帮帮我吗?这让我疯了!
注意:我不能使用WinForms,Qt或其他一些有助于为窗口设置外观的库,这是一个旧项目,所有这些都必须直接在winapi中完成,处理正确的WM_*消息.没有图书馆可以链接.
注意2:在WM_NCACTIVATE消息中使用SetWindowPos或RedrawWindow会产生相同的结果.
这是代码:
#include#include LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); char szClassName[ ] = "SkinTest"; int left_off; int right_off; int top_off; int bottom_off; int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) { HWND hwnd; /* This is the handle for our window */ MSG messages; /* Here messages to the application are saved */ WNDCLASSEX wincl; /* Data structure for the windowclass */ wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; wincl.style = CS_DBLCLKS; wincl.cbSize = sizeof (WNDCLASSEX); wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = NULL; wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; if (!RegisterClassEx (&wincl)) return 0; DWORD style = WS_OVERLAPPEDWINDOW; hwnd = CreateWindowEx ( WS_EX_CLIENTEDGE, szClassName, "Code::Blocks Template Windows App", style, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, HWND_DESKTOP, NULL, hThisInstance, NULL ); // // This prevent the round-rect shape of the overlapped window. // HRGN rgn = CreateRectRgn(0,0,500,500); SetWindowRgn(hwnd, rgn, TRUE); left_off = 4; right_off = 4; top_off = 23; bottom_off = 4; ShowWindow (hwnd, nCmdShow); while (GetMessage (&messages, NULL, 0, 0)) { TranslateMessage(&messages); DispatchMessage(&messages); } return messages.wParam; } #define COLOR_TITLEBAR 0 #define COLOR_LEFT_BORDER 2 #define COLOR_RIGHT_BORDER 4 #define COLOR_BOTTOM_BORDER 6 int win_x, win_y, win_width, win_height; int win_is_not_active = 0; COLORREF borders_colors[] = { RGB(255,255,0), RGB(180,180,0), // Active titlebar - Not active titlebar RGB(0,255,255), RGB(0,180,180), // Active left border - Not active left border RGB(255,0,255), RGB(180,0,180), // Active right border - Not Active right border RGB(255,0,0), RGB(180,0,0) // Active bottom border - Not active bottom border }; void draw_titlebar(HDC hdc) { HBRUSH tmp, br = CreateSolidBrush(borders_colors[COLOR_TITLEBAR + win_is_not_active]); tmp = (HBRUSH)SelectObject(hdc, br); Rectangle(hdc, 0, 0, win_width, top_off); SelectObject(hdc, tmp); DeleteObject(br); } void draw_left_border(HDC hdc) { HBRUSH tmp, br = CreateSolidBrush(borders_colors[COLOR_LEFT_BORDER + win_is_not_active]); tmp = (HBRUSH)SelectObject(hdc, br); Rectangle(hdc, 0, top_off, left_off, win_height - bottom_off); SelectObject(hdc, tmp); DeleteObject(br); } void draw_right_border(HDC hdc) { HBRUSH tmp, br = CreateSolidBrush(borders_colors[COLOR_RIGHT_BORDER + win_is_not_active]); tmp = (HBRUSH)SelectObject(hdc, br); Rectangle(hdc, win_width - right_off, top_off, win_width, win_height - bottom_off); SelectObject(hdc, tmp); DeleteObject(br); } void draw_bottom_border(HDC hdc) { HBRUSH tmp, br = CreateSolidBrush(borders_colors[COLOR_BOTTOM_BORDER + win_is_not_active]); tmp = (HBRUSH)SelectObject(hdc, br); Rectangle(hdc, 0, win_height - bottom_off, win_width, win_height); SelectObject(hdc, tmp); DeleteObject(br); } LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { switch (message) { case WM_DESTROY: PostQuitMessage (0); break; case WM_SIZE: { RECT rect; HRGN rgn; GetWindowRect(hwnd, &rect); win_x = rect.left; win_y = rect.top; win_width = rect.right - rect.left; win_height = rect.bottom - rect.top; // // I use this to set a rectangular region for the window, and not a round-rect one. // rgn = CreateRectRgn(0,0, rect.right, rect.bottom); SetWindowRgn(hwnd, rgn, TRUE); DeleteObject(rgn); } break; case WM_PAINT: { printf("WM_PAINT\n"); PAINTSTRUCT ps; HDC hdc; HBRUSH hb; RECT rect; hdc = BeginPaint(hwnd, &ps); hb = CreateSolidBrush(RGB(rand()%255,rand()%255,rand()%255)); GetClientRect(hwnd, &rect); FillRect(hdc, &rect, hb); DeleteObject(hb); EndPaint(hwnd, &ps); break; } case WM_NCPAINT: { printf("WM_NCPAINT\n"); HDC hdc; HBRUSH br; RECT rect; HRGN rgn = (HRGN)wparam; if ((wparam == 0) || (wparam == 1)) hdc = GetWindowDC(hwnd); else hdc = GetDCEx(hwnd, (HRGN)wparam, DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE|DCX_INTERSECTRGN); draw_titlebar(hdc); draw_left_border(hdc); draw_right_border(hdc); draw_bottom_border(hdc); ReleaseDC(hwnd, hdc); return 0; } case WM_NCACTIVATE: if (wparam) win_is_not_active = 0; else win_is_not_active = 1; // Force paint our non-client area otherwise Windows will paint its own. SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_FRAMECHANGED); //RedrawWindow(hwnd, 0, 0, RDW_FRAME | RDW_UPDATENOW | RDW_NOCHILDREN); return 0; case WM_NCCALCSIZE: { if (wparam) { NCCALCSIZE_PARAMS * ncparams = (NCCALCSIZE_PARAMS *)lparam; printf("WM_NCCALCSIZE wparam:True\n"); ncparams->rgrc[0].left += left_off; ncparams->rgrc[0].top += top_off; ncparams->rgrc[0].right -= right_off; ncparams->rgrc[0].bottom -= bottom_off; return 0; } else { RECT * rect = (RECT *)lparam; return 0; } } case WM_NCHITTEST: { LRESULT result = DefWindowProc(hwnd, message, wparam, lparam); switch (result) { // // I have to set this, because i need to draw my own min/max/close buttons // in different coordinates where the system draws them, so let's consider // all the titlebar just a tilebar for now, ignoring those buttons. // case HTCLOSE: case HTMAXBUTTON: case HTMINBUTTON: case HTSYSMENU: case HTNOWHERE: case HTHELP: case HTERROR: return HTCAPTION; default: return result; }; } case WM_ERASEBKGND: return 1; default: return DefWindowProc (hwnd, message, wparam, lparam); } return 0; }
小智.. 9
你想要适当地处理WM_NCHITTEST,因为Windows也喜欢从该消息中绘制非客户端元素(在默认窗口过程中).
在我的一些自定义窗口中处理此消息后,您所遇到的问题就消失了.
编辑:我看到你已经在处理WM_NCHITTEST了,如果你正在处理一个特定的命中,你不会想要调用DefWindowProc,因为它会尝试绘制这些字幕按钮.