簡體   English   中英

如何在vc++ win32 api中控制滾動條

[英]how to control scrollbar in vc++ win32 api

基本上我有一個可以攜帶其他控件的自定義窗口,比如按鈕、位圖、文本框等,現在的問題是,如果項目超出窗口,我的意思是如果我嘗試創建 20 個按鈕,它顯然會消失窗外,所以我想我應該創建一個滾動條來使窗口能夠滾動。 顯然你不能添加 WS_VSCROLL 或 WS_HSCROLL 因為它的向上和向下箭頭甚至不能點擊但是如果你試圖拖動拇指它會跳回頂部。 所以我想我最好用 createwindow() 函數創建一個滾動條並控制其余的東西。

這是我如何創建窗口本身和滾動條的代碼:代碼:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, "Name", WS_OVERLAPPEDWINDOW/*WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU*/,
  CW_USEDEFAULT, 0, 1014, 585, NULL, NULL, hInstance, NULL);

 if (!hWnd)
{
  return FALSE;
}

WNDCLASSEX wcs;

wcs.cbSize          = sizeof(wcs);
wcs.lpszClassName   = szClassName;
wcs.hInstance       = GetModuleHandle(0);
wcs.lpfnWndProc     = CustWndProc;
wcs.hCursor         = LoadCursor(NULL, IDC_ARROW);
wcs.hIcon           = 0;
wcs.lpszMenuName        = 0;
wcs.hbrBackground   = (HBRUSH)(COLOR_WINDOW+1);
wcs.style           = 0;
wcs.cbClsExtra      = 0;
wcs.cbWndExtra      = 0;
wcs.hIconSm         = 0;

if(!RegisterClassEx(&wcs))
{
    MessageBox(NULL, "Window Registration Failed!", "Error!",
        MB_ICONEXCLAMATION | MB_OK);
    return 0;
}

hwndCtrl = CreateWindowEx(
                     0L, // give it a standard border
                     szClassName,
                     _T("A custom control"),
                     WS_VISIBLE|WS_CHILD|WS_BORDER,
                     0, 0, 0, 0,
                     hWnd,
                     NULL, GetModuleHandle(0), CustWndProc
                   );
ShowWindow (hwndCtrl, SW_SHOW);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}

這是處理其消息的代碼:

LRESULT CALLBACK CustWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
RECT rc = {};
GetClientRect(hwnd, &rc);
const SIZE sz = { rc.right - rc.left, rc.bottom - rc.top };
SCROLLINFO si;

switch(msg)
{
    case WM_MOUSEHOVER: 
        ::MessageBox(hwnd, "Enter", "Info", MB_OK); 

        return 0; 
case WM_CREATE:
    int w , h;
    w = 10;
    h = 10;
    HWND buttons;
    for(h=10;h<500; h+=35){
        buttons = CreateWindow("BUTTON", "How", WS_VISIBLE|WS_CHILD, w, h, 50, 30, hwnd, (HMENU)1231,NULL, NULL);
    }
    int width, height;
    width           = LOWORD(lParam);                                                   // Width Size of hWnd
    height          = HIWORD(lParam);
    Scrollbar  = CreateWindowEx(0L,
                   "SCROLLBAR",
                   NULL, // There is no text to display
                   WS_CHILD | WS_VISIBLE | SBS_VERT,
                   980,
                   47,
                   18,
                   405,
                   hWnd,
                   NULL,
                   hInst,
                   NULL
                );
    return 0;
case WM_INITDIALOG:
    ZeroMemory(&si, sizeof(si));
    si.cbSize = sizeof(si);
    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
    si.nMin   = 0;
    si.nMax   = 1000;
    si.nPage  = 10;
    si.nPos   = 54;
    SetScrollInfo(Scrollbar, SB_CTL, &si, TRUE);
    return TRUE;


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

正如你所看到的,有一些按鈕是用 for 循環自動創建的,然后滾動條本身被創建,現在我不知道如何讓它向下和向上滾動等等。 歡迎所有回復

一個快速而骯臟的例子:

case WM_VSCROLL:
{
    auto action = LOWORD(wParam);
    HWND hScroll = (HWND)lParam;
    int pos = -1;
    if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
        pos = HIWORD(wParam);
    } else if (action == SB_LINEDOWN) {
        pos = g_scrollY + 30;
    } else if (action == SB_LINEUP) {
        pos = g_scrollY - 30;
    } 
    if (pos == -1)
        break;
    WCHAR buf[20];
    SCROLLINFO si = { 0 };
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_POS;
    si.nPos = pos;
    si.nTrackPos = 0;
    SetScrollInfo(hWnd, SB_VERT, &si, true);
    GetScrollInfo(hWnd, SB_VERT, &si);
    pos = si.nPos;
    POINT pt;
    pt.x = 0;
    pt.y = pos - g_scrollY;
    auto hdc = GetDC(hWnd);
    LPtoDP(hdc, &pt, 1);
    ReleaseDC(hWnd, hdc);
    ScrollWindow(hWnd, 0, -pt.y, NULL, NULL);
    g_scrollY = pos;
    return 0;
}
case WM_CREATE:
{
    for (int i = 0; i < 100; ++i) {
        auto hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"",
            WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 10, 30 * i, 250, 21, hWnd, NULL, hInst, NULL);
        wchar_t buf[10];
        StringCchPrintf(buf, 10, L"%d", i);
        SetWindowText(hEdit, buf);
    }
    RECT rc = { 0 };
    GetClientRect(hWnd, &rc);
    SCROLLINFO si = { 0 };
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_ALL;
    si.nMin = 0;
    si.nMax = 30 * 99 + 21;
    si.nPage = (rc.bottom - rc.top);
    si.nPos = 0;
    si.nTrackPos = 0;
    SetScrollInfo(hWnd, SB_VERT, &si, true);
    return 0;
}

影響:

滾動效果

完整代碼: http : //pastebin.com/byE1xFsb

由於OP無法理解代碼,這里是解釋:

  1. 整個窗口的高度應該是 30 * 99 + 21。99 是編輯計數,21 是編輯控件的高度。

  2. 窗口的滾動范圍應該是[0, 30 * 99 + 21 - client-area-height] 30 * 99 + 21 - client-area-height + client-area-height等於30 * 99 + 21 ,即窗口的整個高度。

  3. 為確保上述滾動范圍,

     si.nMax = 30 * 99 + 21; si.nPage = (rc.bottom - rc.top);

這里有一個來自 microsot例子,它用文本來做,但它可以很容易地適應固定高度的組件。 這篇文章how-to-control-scrollbar-in-vc-win32-api完全符合問題的要求並且有一個很好的可讀代碼。

我知道這已經晚了 5 年,但是如果有人來看,我想至少能回答 OP 的第二個問題(正如 Jichao 回答原始問題)會很好。

哦,男孩簡直不敢相信它幾乎不起作用,仍然存在一些問題,例如滾動有限制,。 我的意思是它可以顯示大約 50 或一些行,即使我沒有那么多項目,也沒有更多,到目前為止我知道問題是 si.nMax = 30 * 99 + 21; 這告訴它限制為 30 * 99 + 21 的總和,但必須有很多東西必須使用窗口線並僅顯示正確的數量。

所以窗口的提前截止實際上是由於“GetClientRect(hWnd, &rc);”。 我在自己的項目中發現將其更改為“GetWindowRect(hWnd, &rc);” 並在創建(或調整大小)窗口后設置 RECT 解決了該問題。

//Called after Window Creation (not in WM_Create! After CreateWindowEx called!) or during Resize.
void ResetScrollbarSize(HWND hWnd)
{
    RECT rc = { 0 };
    GetWindowRect(hWnd, &rc);
    SCROLLINFO si = { 0 };
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_ALL;
    si.nMin = 0;
    si.nMax = 30 * 99 + 21;
    si.nPage = (rc.bottom - rc.top);
    si.nPos = 0;
    si.nTrackPos = 0;
    SetScrollInfo(hWnd, SB_VERT, &si, true);
}

該功能應該可以解決問題。 同樣,重要的部分是在調用並完成 CreateWindowEx之后使用它,因此窗口有一個 RECT 可以獲取。

下面是 Jichao 的改編代碼,用於與孩子一起滾動靜態。

// hStaticWnd should have WS_CLIPCHILDREN flag

#define WMU_SET_SCROLL_HEIGHT WM_USER + 100 // custom message
...
SetWindowLong(hStaticWnd, GWL_WNDPROC, (LONG)&cbNewScroll);
...
SendMessage(hColumnsWnd, WMU_SET_SCROLL_HEIGHT, 500, 0); 
...
SendMessage(hColumnsWnd, WMU_SET_SCROLL_HEIGHT, 0, 0); // to turn off the scroll

LRESULT CALLBACK cbNewScroll(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_VSCROLL: {
            WORD action = LOWORD(wParam);
            int scrollY = (int)GetProp(hWnd, TEXT("SCROLLY"));

            int pos = action == SB_THUMBPOSITION ? HIWORD(wParam) :
                action == SB_THUMBTRACK ? HIWORD(wParam) :
                action == SB_LINEDOWN ? scrollY + 30 :
                action == SB_LINEUP ? scrollY - 30 :
                -1;

            if (pos == -1)
                break;

            SCROLLINFO si{0};
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_POS;
            si.nPos = pos;
            si.nTrackPos = 0;
            SetScrollInfo(hWnd, SB_VERT, &si, true);
            GetScrollInfo(hWnd, SB_VERT, &si);
            pos = si.nPos;
            POINT p{0, pos - scrollY};
            HDC hdc = GetDC(hWnd);
            LPtoDP(hdc, &p, 1);
            ReleaseDC(hWnd, hdc);
            ScrollWindow(hWnd, 0, -p.y, NULL, NULL);
            SetProp(hWnd, TEXT("SCROLLY"), (HANDLE)pos);

            return 0;
        }
        break;

        case WM_ERASEBKGND: {
            HDC hDC = (HDC)wParam;
            RECT rc{0};
            GetClientRect(hWnd, &rc);
            HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
            FillRect(hDC, &rc, hBrush);
            DeleteObject(hBrush);

            return 1;
        }
        break;

        case WMU_SET_SCROLL_HEIGHT: { 
            RECT rc = { 0 };
            GetWindowRect(hWnd, &rc);
            SCROLLINFO si = { 0 };
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_ALL;
            si.nMin = 0;
            si.nMax = wParam;
            si.nPage = rc.bottom - rc.top;
            si.nPos = 0;
            si.nTrackPos = 0;

            SetScrollInfo(hWnd, SB_VERT, &si, true);
        }
        break;

        case WM_DESTROY: {
            RemoveProp(hWnd, TEXT("SCROLLY"));
        }
        break;
    }


    return CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM