如何创建Win32控件以包含其他Win32控件?

时间:2013-02-04 22:56:42

标签: windows winapi visual-c++

设置:几年前,我们开发了一个很好的C ++跨平台,它管理了许多在Mac OS X Windows之间编写公共代码源的问题。 (我们不会陷入这种方法的巨大缺点 - 我们在1993年开发了它!)。

为了简化可重用组件的开发,我们最近添加了一个“窗格”概念来包含多个控件和用户项,主要处理绘图和其他事件的分层特性,例如击键和鼠标点击。

我们在Mac OS X(Carbon)方面成功构建了这种方法。然而,试图将这种方法转移到Windows(XP SP3及更高版本)导致了无数的混乱问题:不间断地重新绘制窗口内容,并且事件没有传递到我们的“窗格”中。

在Windows上,每个窗格都转换为“窗口”,我怀疑这可能是问题的根源:重叠“封闭”项目下面的项目可能会干扰绘图和事件传播。

是否有可接受的方法以编程方式将控件添加到分组层次结构中?或者是否必须设置特定的FLAG才能完成此任务?

(注意:虽然我们目前与XP SP3兼容,但我们不需要 - 我们可以将最低操作系统定位为Windows 7.我们目前正在开发VS 2010)

斯蒂芬

2 个答案:

答案 0 :(得分:1)

可以遵循以下步骤进行实施:

  1. 在创建父控件(即窗格)时使用WS_CLIPSIBLING | WS_CLIPCHILDREN标志。
  2. 在消息循环中避免WM_ERASEBKGND消息,并为此返回TRUE。这取决于用法。 -https://www.codeproject.com/articles/2078/guide-to-win32-paint-for-intermediates
  3. 有自定义图形时,请使用双缓冲图形。 -http://www.winprog.org/tutorial/bitmaps.html
  4. 要处理子窗口消息,请使用MFC方法,即先让子窗口处理消息。这可以通过覆盖windowproc来完成。

答案 1 :(得分:0)

关于它通常如何运作的简单细分。

当您创建包含子项的父窗口(以下称为“窗格”)时,闪烁通常是由处理WM_ERASEBKGND消息的父窗口的窗口过程引起的,并且在之前绘制所有子窗口的“顶部”指导孩子们重绘自己。

正如Nik Bougalis所说,在创建父窗格时,如果使用CS_CLIPCHILDREN样式创建它,则DefWindowProc()不会在任何窗格的子边界(rects或region)的范围内执行绘制。因此,子窗口或控件占用的“屏幕空间”完全是子控件本身的责任。

对于大多数标准Windows控件,这很好。这将解决闪烁问题。

至于消息: 子窗口每个都有自己的WM_MOUSEMOVE,WM_LBUTTONDOWN,WM_KEYDOWN(以及其他)消息。 Windows将这些消息发送到具有焦点的实际窗口。

如果您希望父窗口获取这些内容,则必须执行一些称为子窗口(控件)的窗口过程的子类。然后在你的新WndProc()中,你将拥有一个你想要捕获的消息的处理程序,并将它们发送到父窗格的HWND。

以下是在控件中嵌入控件的简单工作示例。它显示了子类并将消息传回上游。

右键单击两个孩子中的任何一个,EDIT控件或BUTTON,在蓝色的“窗格”中!!

#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消息 答:这是处理 B.这是标识符 C.通知代码(事件),在本例中为EN_CHANGE。

因此,您可以截取这些消息并通过SendMessage()将它们转发到任何地方。

当您进入自定义绘图控件时,您需要了解以下内容:

  1. 设备上下文是什么。
  2. 处理WM_PAINT消息。
  3. 也许是WM_PRINTCLIENT消息。
  4. 的BitBlt()
  5. 也许记忆设备上下文。
  6. 内存设备上下文基本上是您无法进行绘图操作的地方。像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);
    

    无论如何,要了解它有很多东西,但我希望上面的小程序可以帮助你并给你一个模板来玩它!