简体   繁体   中英

how to control scrollbar in vc++ win32 api

basically i have a custom window which can carry other controls, let's say buttons, bitmaps, text boxes and etc, now the thing is that if the items went out of window, i mean if i tried to create like 20 button obviously it will go out of window, so i thought i should create a scroll bar to make the window scroll able. obviously you can't add WS_VSCROLL or WS_HSCROLL cause the up and down arrow of it won't be even clickable but if you tried to drag the thumb it will jump just back to the top. so i thought i better create a scroll bar with createwindow() function and control the rest of stuff.

Here is the code how i create the window itself and the scroll bar: Code:

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;
}

here is the code for handling its messages:

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;
}

as you can see there are some buttons which is automatically being created with for loop and after that the scroll bar itself is being created, now i don't know how to make it scroll down and up and so and so forth. All replies are welcomed

A quick and dirty example:

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;
}

Effect:

滚动效果

Full code: http://pastebin.com/byE1xFsb

Since OP cannot understand the code, here is the explanation:

  1. The whole height of the window should be 30 * 99 + 21. 99 is the edit count, 21 is the edit control's height.

  2. The scroll range for the window should be [0, 30 * 99 + 21 - client-area-height] . 30 * 99 + 21 - client-area-height + client-area-height equals to 30 * 99 + 21 which is the whole height of the window.

  3. To ensure the above scroll range,

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

Here an example from microsot that do it with text but it can be easily adapted to components of fixed height. And this article how-to-control-scrollbar-in-vc-win32-api which matches exactly the requirements of the question and has a nice readable code.

I know it's 5 years too late, but if anyone comes looking, I figured it'd be nice to at least have an answer to the OP's second question (as Jichao answered the original).

oh boy can't believe it barely worked, there are still few problems like there is a limit with scrolling, . i mean it can show around 50 or something line even if i don't have that much of item and no more, sofar i know the problem is si.nMax = 30 * 99 + 21; which tell it to be limited up to sum of 30 * 99 + 21 but there has to be something much must get the window line be used and show only that much right.

So the early-cutoff on the window is actually due to "GetClientRect(hWnd, &rc);". I found in my own projects that changing it to "GetWindowRect(hWnd, &rc);" and having it set the RECT after the Window was created (or resized) solved the issue.

//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);
}

That function should do the trick. Again, the important part it to use it after CreateWindowEx is called and completed, so the Window has a RECT to get.

Below is Jichao's adapted code to scroll a static with children.

// 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM