【问题标题】:WinAPI - get a scrollable frame for drawingWinAPI - 获取可滚动的绘图框架
【发布时间】:2019-07-03 08:17:58
【问题描述】:

我正在编写一种算法,该算法通过凉爽的天体计算序列,作为一项夏季工作。算法本身已经完成,我开始研究使用 C++ 制作一个简单的 GUI,您可以在其中为特定的冷却器组合创建序列,并能够事先查看它。这不是我以前做过的任何事情。

我有一些相当简单的工作,现在我直接在主窗口上绘制序列的外观。 在同一个主窗口中将其绘制到可滚动的“框架”中会有什么困难吗? 现在更大的组合太大了,无法放在同一个屏幕上,仅仅扩大窗口是不够的。 感谢您的帮助!

我尝试使用样式“WS_VSCROLL”制作静态并使用“hdc = GetDC(hWndNewStatic)”,我在抽签中使用了一小段时间,然后它就不再工作了。滚动条不起作用。

example program pic

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{

    WNDCLASSEX wcex = {};
    wcex.cbClsExtra = 0;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.cbWndExtra = 0;
    wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE (IDI_ICON));
    wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE (IDI_ICON));
    wcex.hInstance = hInstance;
    wcex.lpfnWndProc = WndProc;
    wcex.lpszClassName = TEXT("WinApp");
    wcex.lpszMenuName = NULL;
    wcex.style = CS_HREDRAW | CS_VREDRAW;

    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL, TEXT("RegisterClassEx Failed!"), TEXT("Error"), MB_ICONERROR);
        return EXIT_FAILURE;
    }

    HWND hWnd = CreateWindow(
        TEXT("WinApp"), TEXT("SeqGen"),
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT,
        NULL, NULL, hInstance, NULL
    );

    if (!hWnd)
    {
        MessageBox(NULL, TEXT("CreateWindow Failed!"), TEXT("Error"), MB_ICONERROR);
        return EXIT_FAILURE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    hdc = GetDC(hWnd);

    // Messages
    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (IsDialogMessage(hWnd, &msg))
        {}
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return EXIT_SUCCESS;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{

    LPCSTR moduleValue;
    static int index {};
    static unsigned module {0};
    static bool draw {false};

    switch (msg)
    {
        case WM_CREATE:
            hWndStatic = CreateWindow(
                TEXT("Static"), TEXT("Tube rows"),
                WS_CHILD | WS_VISIBLE,
                30, 20, 100, 24,
                hWnd, NULL, NULL, NULL
            );

            hWndStatic = CreateWindow(
                TEXT("Static"), TEXT("Water passes"),
                WS_CHILD | WS_VISIBLE,
                130, 20, 100, 24,
                hWnd, NULL, NULL, NULL
            );

            hWndStatic = CreateWindow(
                TEXT("Static"), TEXT("Multiples"),
                WS_CHILD | WS_VISIBLE,
                230, 20, 100, 24,
                hWnd, NULL, NULL, NULL
            );

            hWndrr = CreateWindowEx(
                WS_EX_CLIENTEDGE, TEXT("Edit"), TEXT(""),
                WS_CHILD | WS_VISIBLE | WS_TABSTOP,
                30, 50, 50, 24,
                hWnd, NULL, NULL, NULL
            );

            hWndvv = CreateWindowEx(
                WS_EX_CLIENTEDGE, TEXT("Edit"), TEXT(""),
                WS_CHILD | WS_VISIBLE | WS_TABSTOP,
                130, 50, 50, 24,
                hWnd, NULL, NULL, NULL
            );

            hWndN = CreateWindowEx(
                WS_EX_CLIENTEDGE, TEXT("Edit"), TEXT("0"),
                WS_CHILD | WS_VISIBLE | WS_TABSTOP,
                230, 50, 50, 24,
                hWnd, NULL, NULL, NULL
            );

            hWndList = CreateWindow(
                TEXT("ListBox"), TEXT(""),
                WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL | WS_BORDER | LBS_NOTIFY,
                20, 150, TEXTBOX_WIDTH, TEXTBOX_HEIGHT,
                hWnd, (HMENU) LST_RESULT, NULL, NULL
            );


            hWndCreateall = CreateWindow(
                TEXT("Button"), TEXT("Create all DIR"),
                WS_CHILD | WS_VISIBLE,
                50, 90, 100, 24,
                hWnd, (HMENU) BTN_CREATEALL, NULL, NULL
            );   

            hWndButton = CreateWindow(
                TEXT("Button"), TEXT("Create DIR"),
                WS_CHILD | WS_VISIBLE | WS_TABSTOP,
                200, 90, 100, 24,
                hWnd, (HMENU) BTN_CREATE, NULL, NULL
            );   

            break;

        case WM_COMMAND:
            switch (HIWORD(wParam))
            {
                case LBN_SELCHANGE:
                    if (LOWORD(wParam) == LST_RESULT)
                    {
                        index = SendMessage(hWndList, LB_GETCURSEL, 0, 0);

                        if (index >= 0)
                        {
                            module = 0;
                            moduleValue = (to_string(module).c_str());
                            SetWindowText(hWndStaticModule, moduleValue);
                            draw = true;
                            RedrawWindow(hWnd, 0, 0, RDW_INVALIDATE);
                        }
                    }
                    break;

                case BN_CLICKED:
                    switch (LOWORD(wParam))
                    {
                        case BTN_CREATE:
                            --stuff for creating combination--

                            break;

                        case BTN_CREATEALL:
                            --stuff for creating all possible combination--

                            break;
                    }

                    break;

                default:
                    break;
            }
            break;

        case WM_PAINT:
            if (draw)
            {
                --function for drawing stuff on main window--

                draw = false;
            }
            UpdateWindow(hWnd);

            break;

        case WM_DESTROY:
            PostQuitMessage(EXIT_SUCCESS);

        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return FALSE;
}

【问题讨论】:

  • 提示:我看到你调用 UpdateWindow 来响应 WM_PAINT,你应该避免它,因为它会生成一个新的 WM_PAINT,而且,在 win32 应用程序中,应该在 WM_PAINT 中获取 hdc。使用 BeginPaint 完成,绘制后应使用 EndPaint 处理。
  • 您还可以嵌入更高级的控件,例如 FlowDocumentScrollViewer,您可以在其中添加任何内容:Test FlowDocumentScrollViewer

标签: c++ winapi frame scrollable


【解决方案1】:

你需要大量的代码来实现你需要的东西,我只能给你一个理论上的解释需要的组件。

  1. 面板类。面板需要自己的窗口类和自己的 windowproc。面板 proc 应该处理 WM_VSCROLL,也许是 WM_HSCROLL手动 使用 SetScrollPos 设置新的滚动值并将其存储在某个状态变量中。您的 WM_*SCROLL 处理程序现在有两个选项:
    • 调用InvalidateRect,让WM_PAINT刷新整个面板。
    • 调用ScrollWindow 移动实际图形,然后调用UpdateWindow 刷新未覆盖区域。
  2. 面板绘制算法。您应该像往常一样对WM_PAINT 绘制模型做出反应,但是 y 坐标必须通过滚动状态变量移动。你有几个选择:
    • 重绘整个窗口。
    • 在离屏 HDC 上绘制新窗口内容,然后在整个窗口上进行 bitblit。
    • 重绘未被覆盖的区域(参见上一点关于ScrollWindow
  3. 面板实例在主窗口中。主 win 会将面板分配为子窗口,就像您为其他控件所做的那样,还指定 WS_VSCROLL(和 WS_HSCROLL?)并根据文档大小使用 SetScrollInfo 配置滚动大小。主赢还需要对WM_SIZE 做出反应以调整(MoveWindow)面板的大小,赋予它新的大小。新尺寸的计算方法是从主要胜利的客户区域中减去您喜欢的填充。

每一点最终都需要进一步谷歌搜索并在 SO 上发布。

您也可以考虑使用 OpenGL、DirectX 或仅使用 GDI+ 进行绘图(这反过来可能会引发很多其他问题)。

【讨论】:

  • 好吧,我猜这不会太容易。我会调查一下。非常感谢您的提示!
  • 很高兴您发现它很有用,欢迎来到 Stack Overflow。如果此答案解决了您的问题,请将其标记为已接受。
猜你喜欢
  • 1970-01-01
  • 2021-11-20
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多