7

セットアップ:数年前、Mac OSXWindows間で共通コードソースを作成する際の問題の多くを管理する優れたC++クロスプラットフォームを開発しました。(このアプローチの大きな欠点については触れません。これは1993年に開発しました!)。

再利用可能なコンポーネントの開発を簡素化するために、最近、複数のコントロールとユーザーアイテムを含む「ペイン」の概念を追加しました。これは、基本的に、描画の階層的な性質や、キーストロークやマウスクリックなどの他のイベントを処理します。

このアプローチは、Mac OS X(カーボン)側でうまく設計されました。ただし、そのアプローチをWindows(XP SP3以降)に移行しようとすると、ウィンドウコンテンツのノンストップの再描画や、イベントが「ペイン」に渡されないなど、無数の厄介な問題が発生します。

Windowsでは、各ペインは「ウィンドウ」に変換されます。これが問題の原因である可能性があります。囲んでいるアイテムの「下」にあるアイテムを重ねると、描画やイベントの伝播が妨げられる可能性があります。

グループ化された階層にプログラムでコントロールを追加するための受け入れられた方法はありますか?または、これを達成するために設定する必要がある特定のフラグはありますか?

(注:現在、XP SP3と互換性がありますが、互換性がある必要はありません。最小のOSをWindows 7としてターゲットにすることができます。現在、VS 2010で開発中です)

スティーブン

4

2 に答える 2

3

それが通常どのように機能するかの簡単な内訳。

子を含む親ウィンドウ (以下、"ペイン" と呼びます) を作成する場合、ちらつきは通常、親ウィンドウのウィンドウ プロシージャが 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() を介してどこにでも転送できます。

カスタム描画コントロールを使用する場合は、次のことを知っておく必要があります。

  1. デバイス コンテキストとは。
  2. WM_PAINT メッセージの処理。
  3. おそらく WM_PRINTCLIENT メッセージです。
  4. BitBlt()
  5. おそらくメモリデバイスコンテキスト。

メモリ デバイス コンテキストは、描画操作を行うための目に見えない場所です。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);

とにかく、それについて学ぶことはたくさんありますが、上記の小さなプログラムがあなたを助け、それをいじるためのテンプレートを提供してくれることを願っています!

于 2013-09-13T09:36:23.223 に答える