尽管 SIF_DISABLENOSCROLL 滚动条不可见

Scroll bar doesn't become visible despite SIF_DISABLENOSCROLL

考虑带有 WS_HSCROLL and/or WS_VSCROLL 滚动条的 window。通常,如果滚动范围太小,Windows 会自动使这些滚动条不可见 (nPage <= nMax)。

如果window当前由于滚动范围太小而处于滚动条不可见的状态,并且SetScrollInfo带有SIF_DISABLENOSCROLL标志用于force the scroll bars to be visible, but disabled,那么调用似乎没有任何效果。

如果在滚动条当前可见时设置 SIF_DISABLENOSCROLL(由于滚动范围足够大),则它会按预期工作。

这里有一个简单的切换来演示这个问题:

SCROLLINFO ScrollInfo = {};
ScrollInfo.cbSize = sizeof(ScrollInfo);
if (WantScrollBarsVisible)
{
    ScrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE;
    ScrollInfo.nPage = 100;
    ScrollInfo.nMax = 1; // Smaller value than nPage, so that the scroll bars should be disabled
    // NOTE: If nMax is bigger than nPage, the scroll bars do become visible as expected.
    SetScrollInfo(hWnd, SB_VERT, &ScrollInfo, true);
    SetScrollInfo(hWnd, SB_HORZ, &ScrollInfo, true);
    // Bugged!! Scroll bars are still invisible.
    // They *happen* to become visible as soon as the window is being resized.
}
else
{
    ScrollInfo.fMask = SIF_PAGE | SIF_RANGE;
    // This makes the scroll bars effectively invisible (nMin == nMax == nPage == 0)
    SetScrollInfo(hWnd, SB_VERT, &ScrollInfo, true);
    SetScrollInfo(hWnd, SB_HORZ, &ScrollInfo, true);
}

SetScrollInfo 之后调用 ShowScrollBar(..., true) 也不起作用。 InvalidateRectUpdateWindow.

也没有

有趣的是,也没有 WM_SIZE 消息,正如您在 SetScrollInfo 调用后通常预期的那样,因为如果滚动条出现(或消失),客户区会缩小。

这似乎是 Windows 中的错误。

https://microsoft.public.vc.mfc.narkive.com/7gXZ62cm/sif-disablenoscroll

事实证明,如果滚动条 当前不可见 无论出于何种原因 (1),请使用 SetScrollInfoSIF_DISABLENOSCROLL 永远不会让它可见。

之所以最初有效,是因为在window创建时,滚动条恰好有一些默认值,可以使其可见。调整 window 的大小也恰好使滚动条可见,如果它们错误地不可见的话。可能是部分系统更新了一堆滚动参数,然后突然意识到滚动条毕竟应该是可见的。

上面的文章包含一个解决方法:在使用 SetScrollInfoSIF_DISABLENOSCROLL 之前,首先设置一些滚动参数(使用 SetScrollInfo 而没有 SIF_DISABLENOSCROLL) 肯定 会导致显示滚动条。您需要每次您认为滚动条可能目前不可见。

我发现这很尴尬,想出了这个:

SCROLLINFO ScrollInfo = {};
ScrollInfo.cbSize = sizeof(ScrollInfo);
if (WantScrollBarsVisible)
{
    // ======================================================================
    // Use ShowScrollBar to ensure that the scroll bars are visible *BEFORE*
    // calling SetScrollInfo with SIF_DISABLENOSCROLL.
    ShowScrollBar(hWnd, SB_VERT, true);
    ShowScrollBar(hWnd, SB_HORZ, true);
    // ======================================================================

    ScrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE;
    ScrollInfo.nPage = 100;
    ScrollInfo.nMax = 1; // Smaller value than nPage, so that the scroll bars should be disabled
    SetScrollInfo(hWnd, SB_VERT, &ScrollInfo, true);
    SetScrollInfo(hWnd, SB_HORZ, &ScrollInfo, true);
}
else
{
    ScrollInfo.fMask = SIF_PAGE | SIF_RANGE;
    // This makes the scroll bars effectively invisible (nMin == nMax == nPage == 0)
    SetScrollInfo(hWnd, SB_VERT, &ScrollInfo, true);
    SetScrollInfo(hWnd, SB_HORZ, &ScrollInfo, true);
}

通常,ShowScrollBar 不是很有用,因为它的效果是不稳定的 - 只要使用 SetScrollInfo 更改滚动参数,它们就会被覆盖。但是,在这种情况下,只要确保在 SIF_DISABLENOSCROLL 出现之前滚动条可见就足够了。

如果可以直接与滚动条交互,甚至可能有更好的方法(比如告诉他们立即更新自己)。

(1) 我测试过的可能原因包括: Window styles WS_VSCROLL/WS_HSCROLL being removed从 window 创建后;以前 nMax-nMin<=nPage(滚动范围太小)没有 SIF_DISABLENOSCROLLShowScrollBar(..., false).