当我调用SetScrollInfo时,如何阻止Win32将相反栏的滚动范围设置为100?

时间:2016-03-14 19:37:08

标签: winapi

检查一下!这适用于任何Win32应用程序。我正在使用MFC,但我不认为它特定于该框架。

我在各种窗口上都重现了它,它似乎与样式,扩展样式或类风格无关。

以下是正在发生的事情:如果一个窗口有没有滚动条,并且我在其中一个滚动方向(例如水平)上设置了一个范围,那么其他方向(例如垂直方向)现在内部范围太大!我说“内部”因为它仍然不可见,谢天谢地,但是范围的存在使事情变得混乱。

伪代码中最直接的演示:

GetScrollInfo(SB_HORZ); // min=0, max=0   --as expected
GetScrollInfo(SB_VERT); // min=0, max=0   --as expected
SetScrollInfo(SB_HORZ, 0, 5);
GetScrollInfo(SB_HORZ); // min=0, max=5   --as expected
GetScrollInfo(SB_VERT); // min=0, max=100 --what??

亲自尝试,这太疯狂了!什么更加疯狂和令人沮丧,以及我如何在第一时间遇到这个问题,如果你尝试使用SetScrollPos()它会让你!例如,如果您在之前调用了SetScrollPos(SB_VERT, 3) ,那么GetScrollPos(SB_VERT)将返回0,但如果我在之后执行了,那么它将返回 3 !再次注意,我们正在谈论SB_VERT,它从未被设置为具有范围!

感觉就像我必须遗漏一些东西,特别是考虑到相反的滚动条(具有这个莫名其妙的范围最大值100,在上面的例子中是垂直的)实际上并没有出现在屏幕上。

(我尝试设置/获取许多不同组合的滚动信息以尝试诊断这个疯狂的问题,但它总是回到行为,如果你没有滚动条,然后你只设置一个,那么另一个去100.)

(那个数字(100)来自哪里?)

EDIT1:以下是实际代码:

void func1(HWND hWnd)
{
    // this line logs: get(h) 0(1447)->0-0,0
    { SCROLLINFO si = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE | SIF_POS }; SetLastError(0); int result = ::GetScrollInfo(hWnd, SB_HORZ, &si); TRACE(_T("get(h) %li(%li)->%li-%li,%li\n"), result, GetLastError(), si.nMin, si.nMax, si.nPage); }

    // this line logs: get(v) 0(1447)->0-0,0
    { SCROLLINFO si = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE | SIF_POS }; SetLastError(0); int result = ::GetScrollInfo(hWnd, SB_VERT, &si); TRACE(_T("get(v) %li(%li)->%li-%li,%li\n"), result, GetLastError(), si.nMin, si.nMax, si.nPage); }

    // this line logs: set(h) 0(0)->0-5,1
    { SCROLLINFO si = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE, 0, 5, 1 }; SetLastError(0); int result = ::SetScrollInfo(hWnd, SB_HORZ, &si, FALSE); TRACE(_T("set(h) %li(%li)->%li-%li,%li\n"), result, GetLastError(), si.nMin, si.nMax, si.nPage); }

    // this line logs: get(h) 1(0)->0-5,1
    { SCROLLINFO si = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE | SIF_POS }; SetLastError(0); int result = ::GetScrollInfo(hWnd, SB_HORZ, &si); TRACE(_T("get(h) %li(%li)->%li-%li,%li\n"), result, GetLastError(), si.nMin, si.nMax, si.nPage); }

    // this line logs: get(v) 1(0)->0-100,0
    { SCROLLINFO si = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE | SIF_POS }; SetLastError(0); int result = ::GetScrollInfo(hWnd, SB_VERT, &si); TRACE(_T("get(v) %li(%li)->%li-%li,%li\n"), result, GetLastError(), si.nMin, si.nMax, si.nPage); }
}

EDIT2:在此重申一下这个问题:如何处理此行为并维护模块化代码。现在我能找到解决它的唯一方法就是使用这个kludgy函数在对面条状态的check + set中包装对SetScrollInfo()的任何调用:

int MySetScrollInfo(HWND hWnd, int nBar, LPSCROLLINFO psi, BOOL bRedraw)
{
    int nOppositeBar = (nBar == SB_HORZ) ? SB_VERT : SB_HORZ;
    SCROLLINFO siOpposite = { sizeof(SCROLLINFO), SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS };
    ::GetScrollInfo(hWnd, nOppositeBar, &siOpposite);
    int ans = ::SetScrollInfo(hWnd, nBar, psi, bRedraw);
    if (siOpposite.nMin == siOpposite.nMax) {
        DWORD dwErr = GetLastError();
        ::SetScrollInfo(hWnd, nOppositeBar, &siOpposite, bRedraw);
        SetLastError(dwErr);
    }
    return ans;
}

这不可能是正确的。什么是正确的方法??

EDIT3: Blarg!这也不起作用,因为它不仅仅是当你设置的时候相反的是nil,而是当你把它设置为任何东西(包括nil)时,当BOTH刚才为零时。所以我最终做的是覆盖GetScrollInfo和SetScrollInfo。在设置时,我存储一个标志,无论调用者是否设置为nil,然后我在每个Get上检查该标志。真是一团糟。

1 个答案:

答案 0 :(得分:1)

我能够复制声称在一个条上设置范围导致另一个条的范围初始化为0-100。

与可能导致序列错误的应用程序兼容可能会产生副作用。

至于100的来源,GetScrollRange的文档说:

  

标准滚动条的默认范围是0到100。

"标准"在此上下文中,表示非客户区滚动条之一,而不是子滚动条控件。