それが通常どのように機能するかの簡単な内訳。
子を含む親ウィンドウ (以下、"ペイン" と呼びます) を作成する場合、ちらつきは通常、親ウィンドウのウィンドウ プロシージャが WM_ERASEBKGND メッセージを処理し、子に指示する前にすべての子を "上に" 描画することによって発生します。自分自身を再描画します。
Nik Bougalis が述べたように、親ペインを作成するときに CS_CLIPCHILDREN スタイルで作成すると、DefWindowProc() によって実行される描画は、ペインの子の境界 (四角形または領域) の境界内では発生しません。したがって、子ウィンドウまたはコントロールが占有する「画面スペース」は、完全に子コントロール自体の責任です。
ほとんどの標準的な Windows コントロールでは、これで問題ありません。これにより、ちらつきの問題が解決されます。
メッセージについて: 子ウィンドウはそれぞれ、WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_KEYDOWN (その他) メッセージを受け取ります。Windows は、これらのメッセージをフォーカスのある実際のウィンドウに送信します。
親ウィンドウにこれらを取得させたい場合は、子ウィンドウ (コントロール) のウィンドウ プロシージャをサブクラス化するという処理を行う必要があります。次に、新しい WndProc() で、キャッチして親ペインの HWND に送信するメッセージのハンドラーを作成します。
以下は、コントロール内にコントロールを埋め込む簡単な作業例です。これは、メッセージをサブクラス化し、上流に戻すことを示しています。
青い「ペイン」で、2 つの子、EDIT コントロールまたはボタンのいずれかを右クリックします。
#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);
}
ほとんどのネイティブ ウィンドウ コントロールは、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);
とにかく、それについて学ぶことはたくさんありますが、上記の小さなプログラムがあなたを助け、それをいじるためのテンプレートを提供してくれることを願っています!