有没有更好的方法在Win32 API中制作滚动面板?

问题描述 投票:0回答:1

我一直在尝试使用 win32 api 来创建 GUI 问题是我正在尝试制作一个滚动面板,这有点奇怪。 我的意思是它有效,唯一的问题是当我快速滚动或执行某些操作时,顶部面板会出现错误。 对我来说,代码看起来非常复杂。创建一个新窗口,为其创建回调函数。等等..

#include <windows.h>

const char CLASS_NAME[] = "vvlxpClass";
const char SCROLL_PANEL_CLASS_NAME[] = "FunctionScClass";

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ScrollPanelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

typedef struct {
    int thickness;
    COLORREF color;
} XBORDER;

VOID CreatePanel(
    HWND hwnd,
    int x, int y,
    int width, int height,
    COLORREF color,
    XBORDER border
) {
    RECT rect = {x, y, x + width, y + height};
    HDC hdc = GetDC(hwnd);
    HBRUSH brush = CreateSolidBrush(color);
    FillRect(hdc, &rect, brush);
    DeleteObject(brush);

    if (border.thickness > 0) {
        HPEN hPen = CreatePen(PS_SOLID, border.thickness, border.color);
        SelectObject(hdc, hPen);
        SelectObject(hdc, GetStockObject(NULL_BRUSH));
        Rectangle(hdc, x, y, x + width, y + height);
        DeleteObject(hPen);
    }

    ReleaseDC(hwnd, hdc);
}

HWND CreateScrollPanel(
    HWND hwndParent,
    int x, int y,
    int width, int height,
    COLORREF color,
    XBORDER border
) {
    HWND hwndScrollPanel = CreateWindowEx(
        0,
        SCROLL_PANEL_CLASS_NAME,
        NULL,
        WS_CHILD | WS_VISIBLE | WS_VSCROLL,
        x, y, width, height,
        hwndParent,
        NULL,
        (HINSTANCE)GetWindowLongPtr(hwndParent, GWLP_HINSTANCE),
        NULL
    );

    SetWindowLongPtr(hwndScrollPanel, GWLP_USERDATA, (LONG_PTR)color);
    SetWindowLongPtr(hwndScrollPanel, GWLP_WNDPROC, (LONG_PTR)ScrollPanelProc);

    return hwndScrollPanel;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASS wc = { };

    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    wc.lpfnWndProc = ScrollPanelProc;
    wc.lpszClassName = SCROLL_PANEL_CLASS_NAME;

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0,
        CLASS_NAME,
        ":)",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static RECT panelRect = {0, 0, 200, 0};
    static HWND hwndScrollPanel;

    switch (uMsg) {
        case WM_SIZE: {
            int height = HIWORD(lParam);
            panelRect.bottom = height;
            InvalidateRect(hwnd, NULL, TRUE);
        } break;

        case WM_CREATE: {
            XBORDER border = {1, RGB(130, 135, 144)};
            hwndScrollPanel = CreateScrollPanel(hwnd, 0, 1, 199, 200, RGB(255, 255, 255), border);
        } break;

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            XBORDER border = {1, RGB(130, 135, 144)};
            CreatePanel(hwnd, panelRect.left, panelRect.top, panelRect.right, panelRect.bottom, RGB(255, 255, 255), border);

            SetTextColor(hdc, RGB(0, 0, 0));
            SetBkMode(hdc, TRANSPARENT);

            HFONT hFont = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Segoe UI"));
            SelectObject(hdc, hFont);

            TextOut(hdc, 5, 5, "Functions", 9);

            DeleteObject(hFont);

            EndPaint(hwnd, &ps);
        } break;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

LRESULT CALLBACK ScrollPanelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static int scrollPos = 0;
    static int contentHeight = 800;

    switch (uMsg) {
        case WM_VSCROLL: {
            SCROLLINFO si = { sizeof(si), SIF_ALL };
            GetScrollInfo(hwnd, SB_VERT, &si);

            int yPos = si.nPos;

            switch (LOWORD(wParam)) {
                case SB_LINEUP:
                    si.nPos -= 10;
                    break;
                case SB_LINEDOWN:
                    si.nPos += 10;
                    break;
                case SB_PAGEUP:
                    si.nPos -= si.nPage;
                    break;
                case SB_PAGEDOWN:
                    si.nPos += si.nPage;
                    break;
                case SB_THUMBTRACK:
                    si.nPos = si.nTrackPos;
                    break;
            }

            si.fMask = SIF_POS;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            GetScrollInfo(hwnd, SB_VERT, &si);

            if (si.nPos != yPos) {
                scrollPos = si.nPos;
                InvalidateRect(hwnd, NULL, TRUE);
            }
        } break;

        case WM_MOUSEWHEEL: {
            int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
            int scrollLines = 3;
            scrollPos -= (zDelta / WHEEL_DELTA) * scrollLines * 10;

            SCROLLINFO si = { sizeof(si), SIF_RANGE | SIF_POS | SIF_PAGE };
            GetScrollInfo(hwnd, SB_VERT, &si);

            if (scrollPos < si.nMin) scrollPos = si.nMin;
            if (scrollPos > si.nMax - (int)si.nPage + 1) scrollPos = si.nMax - (int)si.nPage + 1;

            si.fMask = SIF_POS;
            si.nPos = scrollPos;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            InvalidateRect(hwnd, NULL, TRUE);
        } break;

        case WM_ERASEBKGND: {
            return 1;
        }

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            COLORREF color = (COLORREF)GetWindowLongPtr(hwnd, GWLP_USERDATA);
            HBRUSH brush = CreateSolidBrush(color);
            RECT rect;
            GetClientRect(hwnd, &rect);
            FillRect(hdc, &rect, brush);
            DeleteObject(brush);

            SetTextColor(hdc, RGB(0, 0, 0));
            SetBkMode(hdc, TRANSPARENT);

            HFONT hFont = CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Segoe UI"));
            SelectObject(hdc, hFont);

            for (int i = 0; i < 10; i++) {
                TextOut(hdc, 10, 25 + i * 20 - scrollPos, "Function", 8);
            }

            CreatePanel(hwnd, 0, 0, 200, 25, RGB(255, 255, 255), (XBORDER){0, 0});
            TextOut(hdc, 5, 5, "Functions", 9);

            DeleteObject(hFont);

            EndPaint(hwnd, &ps);
        } break;

        case WM_SIZE: {
            SCROLLINFO si = { sizeof(si), SIF_RANGE | SIF_PAGE };
            si.nMin = 0;
            si.nMax = contentHeight - 1;
            si.nPage = HIWORD(lParam);
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
        } break;

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

这是我当前的代码,正如我所说,它看起来太复杂了,我不太确定该怎么做。

c user-interface winapi
1个回答
0
投票

尝试使用winapi“列表控件”https://learn.microsoft.com/en-us/windows/win32/controls/list-view-control-reference 我个人曾经尝试过制作一个自定义文本框,并且在滚动方面也遇到了同样的问题,直到我干脆放弃并使用 Windows 自己的 RichEdit 控件和它自己的滚动条来为我做一切。

导致问题的原因只是滚动时调用的 hdc 调用的 InvalidateRects 数量。 您可以使用 ScrollWindowEx,它针对滚动进行了优化,应该可以缓解问题:https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-scrollwindowex,但它是用滚动条校准它并不容易。

我还听说父窗口上的 WS_CLIPCHILDREN 有帮助,但我在你的代码上测试了它,但它没有做任何事情。

© www.soinside.com 2019 - 2024. All rights reserved.