摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P91
SYSMETS2 的功能良好,但是他的效率太差,不足以成为其他程序的范本。下面,我们将示范一个更有效的程序。最令人瞩目的是这个新版本的程序将不会使用我们已经讨论过的 4 个滚动函数。相反的,它将使用 Win32 API 中独有的一些新函数。
4.4.1 滚动条信息函数
滚动条的文档(位于/Platform SDK/User Interface Services/Controls/Scroll Bars)指出 4 个滚动条函数 SetScrollRange、SetScrollPos、GetScrollRange 和 GetScrollPos 已经“过时”。这并不完全准确。尽管这些函数在 Windows 1.0 中就已经存在,但在 Win32 API 中它们已经升级到能接收 32 位的参数。它们依然功能完好,而且似乎还将继续存在下去。更重要的是,它们简单到不会让初学者畏难,这也是为什么我在本书中依然使用它们。
在 Win32 API 中引入的两个新滚动条函数是 SetScrollInfo 和 GetScrollInfo。这两个函数有上述函数的所有功能,而且还加入了两个重要的新功能。
第一个新功能是关于滑块的大小。你也许已经注意到,在 SYSMETS2 中滑块的大小是固定的。然而在你用过的一些 Windows 应用程序中,滑块的大小与显示在窗口中的文档多少是成比例的。在窗口中显示的文档数量称为“页面大小”。下面的公式表明了他们的关系:
GetScrollInfo 函数加入了另一个重要的功能,或者说改进了现有 API 的不足。假设想要滚动条的范围是 65 536 或者更大,在 16 位 Windows 中,这是不可能的。在 Win32 中,函数定义可以接受 32 位的参数,实际上也确实如此。(但是要记住的是,当滚动条的范围有这么大时,滑块的实际位置的个数还是受限于滚动条的物理尺寸,即像素数。)但是,当你收到通知码为 SB_THUMBTRACK 或 SB_THUMBPOSITION 的 WM_VSCROLL 或 WM_HSCROLL 消息时,只有 16 位被用来指定滑块的位置。而 GetScrollInfo 函数可以让你得到实际的 32 位值。
这两个函数的语法如下:
参数 iBar 和在其他滚动条函数中一样,是 SB_VERT 或 SB_HORZ。同样的,它也可以是 SB_CTL,表示一个滚动条控件。SetScrollInfo 的最后一个参数可以是 TRUE 或 FALSE,表示是否希望 Windows 根据新的信息重绘滚动条。
两个函数的第三个参数是一个 SCROLLINFO 结构,定义如下:
程序通常如下定义一个 SCROLLINFO 变量:
在调用这两个函数之前,必须将 cbSize 字段设为该结构的大小:
或者
当熟悉 Windows 应用程序后,会发现还有其他的一些结构也类似地将第一个字段定义为结构的大小。这样,以后的 Windows 版本可以扩充结构而同时保持与以前的应用程序兼容。
fMask 字段是一个或多个以 SIF 为前缀的标志。它们可以用“位或”运算组合在一起。
在 SetScrollInfo 中指定了 SIF_RANGE 时,必须在 nMin 和 nMax 中指定滚动条的范围。当在 GetScrollInfo 中指定了 SIF_RANGE 时,nMin 和 nMax 将返回当前的范围。
SIF_POS 与此类似:在调用 SetScrollInfo 时,须在 nPos 字段中指定滚动条的位置,而 GetScrollInfo 则在 nPos 中返回当前的位置。
SIF_PAGE 可用于指定或获取页面大小。在 SetScrollInfo 中你在 nPage 中指定页面大小。而 GetScrollInfo 在 nPage 中返回页面大小。如果不希望滑块大小发生变化,就不要使用这个标志。
SIF_TRACKPOS 标志只用在 GetScrollInfo 中,而且只在处理通知码是 SB_THUMBTRACK 或 SB_THUMBPOSITION 的 WM_VSCROLL 或 WM_HSCROLL 消息时。在函数返回时,SCROLLINFO 结构的 nTrackPos 字段将返回当前滑块的位置(该值为一个 32 位整数)。
SIF_DISABLENOSCROLL 标志只用在 SetScrollInfo 中。当设定了这个标志后,原来让滚动条不显示的设置这时将禁用滚动条。
SIF_ALL 标志是 SIF_RANGE、SIF_POS、SIF_PAGE 和 SIF_TRACKPOS 的组合。在处理 WM_SIZE 消息,该标志可以使得设定滚动条参数更方便。(在 SetScrollInfo 中,SIF_TRACKPOS 将会被忽略。)同样,处理滚动条消息也变得方便了。
4.4.2 最远可以卷动到哪里?
在 SYSMETS2 中,滚动条的范围设定在 0 和 NUMLINES - 1 之间。当滚动条位置是 0 时,第一行显示在客户区的最上方;当滚动条的位置是 NUMLINES-1 时,最后一行显示在客户区的最上方,而其他行都不可见。
你会说 SYSMETS2 滚动得太多了。实际上,它只需要滚动到最后一行显示在客户区的最下方(而不是在最上方)。我们可以修改 SYSMETS2 来做到这一点。与其在处理 WM_CREATE 消息时设定滚动条的范围,不如等到在收到 WM_SIZE 消息时再来确定滚动条的范围:
假定 NUMLINES 是 75,同时假定这时的窗口中,cyClient/cyChar 是 50。也就是 说,我们有 75 行文字,但是客户区只能显示 50 行。上面的两行代码将滚动条的范围设定为从 0 到 25。当滚动条位置是 0 时,程序显示 0 到 49 行,当滚动条位置是 1 时,显示 1 到 50 行。当滚动条位置是 25(最大值)时,显示 25 到 74 行。显然,我们还需要修改程序的其他地方,但是这是可行的。
新的滚动条函数的另一个好处是:当使用滚动条页面大小时,Windows 自动处理了上面的很多事情。结合 SCROLLINFO 结构和 SetScrollInfo 函数,你只需要类似下面的代码:
当你这样做时,Windows 会将滚动条位置的最大值限制在 si.nMax - si.nPage + 1,而不是简单的 si.nMax。还是用上面的例子:NUMLINES 等于 75(即 si.nMax 等于 74),si.nPage 等于 50。这意味着滚动条位置的最大值是 74 - 50 + 1,也就是 25.这正是我们想要的。
当页面大小和滚动条范围一样大时会怎么样呢?也就是说,在我们的例子里,如果 nPage 是 75 或更大时会怎样?Windows 很聪明地隐藏了滚动条,因为这时并不需要滚动条。如果你不想隐藏滚动条,只需要在调用 SetScrollInfo 时设定 SIF_DISABLENOSCROLL,以让 Windows 显示一个被禁用的滚动条。