它通常如何工作的简单细分。
当您创建包含子窗口的父窗口(以下称为“窗格”)时,闪烁通常是由于父窗口的窗口过程处理 WM_ERASEBKGND 消息,并在指示子窗口之前在其所有子窗口的“顶部”绘制重绘自己。
正如 Nik Bougalis 所说,在创建父窗格时,如果您使用 CS_CLIPCHILDREN 样式创建它,则 DefWindowProc() 执行的任何绘制都不会发生在窗格的任何子边界(矩形或区域)的范围内。因此,子窗口或控件所占用的“屏幕空间”完全是子控件本身的责任。
对于大多数标准 Windows 控件,这很好。这将解决闪烁的问题。
至于消息:孩子们的窗口每个都会收到他们的 WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_KEYDOWN(以及其他)消息。Windows 将这些消息发送到具有焦点的实际窗口。
如果您希望父窗口获得这些,您将不得不做一些称为子类化子窗口(控件)的窗口过程的事情。然后在您的新 WndProc() 中,您将有一个处理程序来处理您想要捕获的消息并将它们发送到父窗格的 HWND。
这是在控件中嵌入控件的简单工作示例。它显示子分类和将消息传递回上游。
右键单击蓝色“窗格”中的两个孩子中的任何一个,编辑控件或按钮!
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <stdio.h>
LRESULT __stdcall WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT __stdcall FrameProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT __stdcall SubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hpi, LPSTR lpcl, int ncs) {
WNDCLASSEX wcex;
MSG msg;
HWND hWnd=0, hFrame=0, hEdit=0, hButton=0, hCheckBox=0;
ATOM ca=0, caframe=0;
RECT cr;
HBRUSH framecolor;
int cx=0;
char *ptr=(char *)&wcex;
int i =0;
for (;i<sizeof(wcex);i++) {
ptr[i]=0;
}
wcex.cbSize=sizeof(wcex);
wcex.hbrBackground = (HBRUSH) COLOR_WINDOW;
wcex.hCursor = LoadCursor(0, IDC_ARROW);
wcex.lpfnWndProc = &WndProc;
wcex.lpszClassName = "mywnd";
wcex.hInstance = hInstance;
wcex.style = CS_HREDRAW|CS_VREDRAW;
ca = RegisterClassEx(&wcex);
for (i=0;i<sizeof(wcex);i++) {
ptr[i]=0;
}
wcex.cbSize=sizeof(wcex);
framecolor = CreateSolidBrush(0xFFA100);
wcex.hbrBackground = (HBRUSH) framecolor;
wcex.hCursor = LoadCursor(0, IDC_ARROW);
wcex.lpfnWndProc = &FrameProc;
wcex.lpszClassName = "myframe";
wcex.hInstance = hInstance;
wcex.style = CS_HREDRAW|CS_VREDRAW;
caframe = RegisterClassEx(&wcex);
hWnd = CreateWindowExA(0, (LPCSTR)ca, "My Window", WS_CLIPCHILDREN|WS_VISIBLE|WS_SYSMENU|WS_SIZEBOX, 100, 100, 500, 500, 0, 0, hInstance, 0);
GetClientRect(hWnd, &cr);
hFrame = CreateWindowExA(0, (LPCSTR)caframe, "", WS_VISIBLE|WS_BORDER|WS_CHILD|WS_CLIPCHILDREN, 10, 10, ((cr.right-cr.left)-20), ((cr.bottom-cr.top)-20), hWnd, (HMENU) 1, hInstance, 0);
cx = ((cr.right-cr.left)-20)/2;
hEdit = CreateWindowExA(0, "Edit", "Edit Control", WS_CHILD|WS_VISIBLE|WS_BORDER, 10, 10, cx, 20, hFrame, (HMENU) 2, hInstance, 0);
hButton = CreateWindowExA(0, "Button", "Click Me!", WS_CHILD|WS_VISIBLE, cx+20, 10, 70, 20, hFrame, (HMENU) 3, hInstance, 0);
/* Sub-Class the children */
SetWindowLongPtr(hEdit, GWLP_USERDATA, GetWindowLongPtr(hEdit, GWLP_WNDPROC));
SetWindowLongPtr(hButton, GWLP_USERDATA, GetWindowLongPtr(hButton, GWLP_WNDPROC));
SetWindowLongPtr(hEdit, GWLP_WNDPROC, (LONG)&SubClassProc);
SetWindowLongPtr(hButton, GWLP_WNDPROC, (LONG)&SubClassProc);
if (!hWnd) {
return -1;
}
while ( GetMessage(&msg, 0, 0, 0) ) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWnd);
DeleteObject(framecolor);
return 0;
}
LRESULT __stdcall WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
RECT rc;
switch (uMsg) {
case WM_WINDOWPOSCHANGING:
GetClientRect(hWnd, &rc);
SetWindowPos(GetDlgItem(hWnd, 1), 0, 0, 0, ((rc.right-rc.left)-20), ((rc.bottom-rc.top)-20), SWP_NOZORDER|SWP_NOMOVE);
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LRESULT __stdcall FrameProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
PAINTSTRUCT ps;
WINDOWPOS *wp=0;
POINT p;
short wmid=0, wmevent=0;
char message[300];
switch (uMsg) {
case WM_WINDOWPOSCHANGING:
wp = (WINDOWPOS *)lParam;
SetWindowPos(GetDlgItem(hWnd, 2), 0, 0, 0, (wp->cx/2), 20, SWP_NOMOVE|SWP_NOZORDER);
SetWindowPos(GetDlgItem(hWnd, 3), 0, (wp->cx/2)+20, 10, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
break;
case WM_RBUTTONDOWN:
p.x = (lParam & 0x0000ffff);
p.y = (lParam >> 16 );
sprintf(message, "The \"frame\" got a WM_RBUTTONDOWN message!\nx: %i\ny: %i\n",
p.x, p.y);
MessageBox(GetParent(hWnd), message, "Message", MB_ICONINFORMATION);
break;
case WM_COMMAND:
wmid = (wParam & 0x0000ffff);
wmevent = wParam>>16;
switch (wmid) {
case 3:
if (wmevent==BN_CLICKED) {
MessageBox(GetParent(hWnd), "You clicked my button!", "Notice", MB_OK);
}
break;
default:
break;
}
break;
case WM_PAINT:
break;
default:
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
LRESULT __stdcall SubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
WNDPROC wp=0;
POINT p;
HWND hParent=0;
char message[300];
wp = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (!wp) {
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
if (uMsg==WM_RBUTTONDOWN) {
p.x = (lParam & 0x0000ffff);
p.y = (lParam >> 16 );
sprintf(message, "Right-Click in control!\nx: %i\ny: %i\n\nNow, we'll convert this to the coordinates of the frame and pass the message up-stream!",
p.x, p.y);
hParent = GetParent(hWnd);
MessageBox(GetParent(hParent), message, "Message", MB_ICONINFORMATION);
ClientToScreen(hWnd, &p);
ScreenToClient(hParent, &p);
SendMessage(hParent, WM_RBUTTONDOWN, wParam, MAKELPARAM(p.x, p.y));
}
return CallWindowProc( wp, hWnd, uMsg, wParam, lParam);
}
大多数本机 Windows 控件通过 WM_COMMAND 或 WM_NOTIFY 消息自动通知其父级。就像在编辑控件中更改文本时一样。它向包含它的父窗口发送一条 WM_COMMAND 消息。
- A - 它的手柄
- B - 这是一个标识符
- C - 通知代码(事件),在本例中为 EN_CHANGE。
因此,您可以拦截这些消息并通过 SendMessage() 将它们转发到任何地方。
当您进入自定义绘图控件时,您需要了解以下内容:
- 什么是设备上下文。
- 处理 WM_PAINT 消息。
- 也许是 WM_PRINTCLIENT 消息。
- BitBlt()
- 也许是内存设备上下文。
内存设备上下文是您进行绘图操作的不可见位置。就像 bkausbk 说的,一个缓冲区。在程序中,我写到我也需要添加一点华丽,我绘制到内存设备上下文。然后,我在 WM_PAINT 事件(来自系统)中使用 BitBlt() 将我的所有窗口及其子项在其上绘制的内存设备上下文复制到要显示的窗口的设备上下文中。
PAINTSTRUCT ps;
case WM_PAINT:
BeginPaint(hWnd, &ps);
BitBlt(ps.hdc, 0, 0, cx, cy, hMemDC, 0, 0, SRCCOPY);
EndPaint(hWnd &ps);
不管怎样,有很多东西要学,但希望上面的小程序可以帮到你,给你一个玩玩的模板!