Winapi 当我调用SetScrolInfo时,如何防止Win32将相对条的滚动范围设置为100?

Winapi 当我调用SetScrolInfo时,如何防止Win32将相对条的滚动范围设置为100?,winapi,Winapi,看看这个!这适用于任何Win32应用程序。我正在使用MFC,但我不认为它是特定于该框架的 我在所有类型的窗口上都复制了这一点,它似乎与样式、扩展样式或类样式无关 发生的情况如下:如果一个窗口没有滚动条,而我只在其中一个滚动方向(例如水平方向)上设置了一个范围,那么另一个方向(例如垂直方向)现在在内部也会有一个范围!我说“内部”是因为它仍然不可见,谢天谢地,但是范围的存在把事情搞砸了 伪代码中最直观的演示: GetScrollInfo(SB_HORZ); // min=0, max=0 --a

看看这个!这适用于任何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??
你自己试试,太疯狂了!更疯狂和令人沮丧的是,我一开始就遇到了这个问题,如果你尝试设置crollpos(),它会让你!因此,例如,如果您在上述操作之前调用了
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:在此重申一个问题:如何处理此行为并维护模块化代码。现在我能找到的唯一解决方法是使用这个笨拙的函数,它将对setScrolInfo()的任何调用包装在一个相反条状态的检查+集合中:

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!这也不起作用,因为这不仅仅是当你设置相反的一个为零时,而是当你设置一个为任何东西(包括零)时,两个之前都为零。所以我最终做的是覆盖GetScrollInfo和SetScrollInfo。设置后,我存储一个标志,以确定调用方是否设置为nil,然后在每次Get时检查该标志。真是一团糟。

我能够重复这样的说法,即在一个条上设置范围会导致另一个条上的范围初始化为0-100

这可能是与顺序错误的应用程序兼容的副作用

至于100的来源,其文档说明:

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


在此上下文中,“标准”是指一个非客户端区域滚动条,而不是子滚动条控件。

显示真实代码,而不是伪代码。是否有正确的顺序?我试图使我的代码模块化,以便当发生某些事情导致内容仅在其垂直范围内更改时,我只调用setScrolInfo(SB_VERT)。你是说我应该总是打两次电话,一次是给某人打电话,一次是给某人打电话,不管我真的需要换哪一个?这意味着先读我不在乎的,然后设定我在乎的,然后设定我不在乎的。哎呀。这不可能是对的。对于滚动条来说,模块化可能不是一个选项。通过“获取顺序错误”,我指的是只初始化其中一个滚动条范围,然后期望两个范围都被设置的坏应用程序。只要您初始化两个滚动条(按任意顺序),这就不会影响您的程序,即使您的程序将它们视为独立的.Hrm,但这不是第一次。任何时候你都可以从两个方向都有一个零的范围到只设置其中一个方向。考虑这个序列获取和设置范围MAX:GET(H);获得(v);组(v,5);组(h,5);get(h);获得(v);set(v,0);set(h,0);组(v,5);get(h);得到(v)。该序列中的六个get调用给出以下六个范围最大值:0;0; 5.5.100; 5.什么!?这扼杀了我的模块性,因为如果内容被设置为两个nil,然后其中一个(例如vert)的内容被设置为非nil,那么setcrollpos(horz)突然就不再受约束了@鲍勃:将滚动条设置为0..0的范围似乎很可疑。该范围为半开放范围,因此如果最大值等于最小值,则没有合法位置。因此,您的选项是(1)不允许0,0,(2)使用滚动条子控件而不是“标准”控件,(3)保存“未接触”栏的设置,然后在更改另一个栏后恢复它们。该范围不是0..0。更仔细地查看输出:对GetScrollInfo的初始调用失败,出现错误\u NO\u SCROLLBAR。基本上,滚动条默认为0..100。如果您在设置任何内容之前询问,可能会被告知“您询问得太早了”,但当您尝试更改任何内容时,将应用默认范围0..100。所以你应该表现得好像这个范围一直是0..100。