【问题标题】:how to control scrollbar in vc++ win32 api如何在 vc++ win32 api 中控制滚动条
【发布时间】:2015-11-12 16:18:23
【问题描述】:

基本上我有一个可以携带其他控件的自定义窗口,比如按钮、位图、文本框等,现在的问题是,如果项目超出窗口,我的意思是如果我尝试创建像 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 循环自动创建的,然后滚动条本身被创建,现在我不知道如何让它向下和向上滚动等等。 欢迎大家回复

【问题讨论】:

  • 处理 WM_HSCROLL 或 WM_VSCROLL 消息,并使用 ScrollWindow 更新您的客户区。
  • 对每个人来说都不是很明显:There are two types of scrollbars。 @Jichao 提出的解决方案适用于通过 WS_VSCROLLWS_HSCROLL 窗口样式附加到窗口的滚动条。
  • 嘿伙计,你能帮我解决我在下面评论框中解释的问题>

标签: c++ visual-studio api winapi


【解决方案1】:

一个快速而肮脏的例子:

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

【讨论】:

  • 感谢您的回复,我可以试一试,看看会发生什么,希望它有效。
  • 天哪,简直不敢相信它几乎没有用,仍然有一些问题,比如滚动有限制,.我的意思是它可以显示大约 50 行或其他行,即使我没有那么多项目并且没有更多,到目前为止我知道问题是 si.nMax = 30 * 99 + 21;这告诉它被限制为 30 * 99 + 21 的总和,但必须有很多东西必须使用窗口线并且只显示那么多正确。而且您不能单击轨迹栏位置本身我的意思是我可以使用向上和向下箭头并可以拖动拇指,但可以单击滚动页面或其他内容的轨迹栏位置本身。
  • 很抱歉,如果您能帮助我解决我上面指定的问题,我将不胜感激。非常感谢你
  • 有人知道该怎么做吗?
  • 哦,来吧,有人告诉我该怎么做。
【解决方案2】:

这里有一个example from microsot,它使用文本来完成,但它可以很容易地适应固定高度的组件。而这篇how-to-control-scrollbar-in-vc-win32-api的文章正好符合题主的要求,代码可读性很好。

【讨论】:

    【解决方案3】:

    我知道已经晚了 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 之后使用它的重要部分,因此 Window 有一个 RECT 可以获取。

    【讨论】:

      【解决方案4】:

      下面是 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);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-26
        • 1970-01-01
        • 2012-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-10
        • 1970-01-01
        相关资源
        最近更新 更多