ps:您可以转载,但请注明出处;你可以修改,但请将修改结果告诉我。
Building a Better Scroll创建更好的滚动条
SYSMETS2 works well, but it's too inefficient a model to be imitated in other programs. Soon I'll present a new version that corrects its deficiencies. Most interesting, perhaps, is that this new version will not use any of the four scroll bar functions discussed so far. Instead, it will use new functions unique to the Win32 API.
SYSMETS2 工作得很好,但它是太低效而不能在其它程序中模仿的一个模型。不久我将介绍一个纠正它的低效的新版本。更有趣的也许是这个新版本将不用这四个到目前为止讨论的滚动条函数的任何一个。代替的是,它将使用对 Win32 API 唯一的新函数。
The Scroll Bar Information Functions滚动条信息函数
The scroll bar documentation (in
/Platform SDK/User Interface Services/Controls/Scroll Bars) indicates that the SetScrollRange, SetScrollPos, GetScrollRange, and GetScrollPos functions are "obsolete." This is not entirely accurate. While these functions have been around since Windows 1.0, they were upgraded to handle 32-bit arguments in the Win32 API. They are still perfectly functional and are likely to remain functional. Moreover, they are simple enough not to overwhelm a newcomer to Windows programming at the outset, which is why I continue to use them in this book.
滚动条文档(在 /Platform SDK/User Interface Services/Controls/Scroll Bars 中)指出 SetScrollRange,SetScrollPos,GetScrollRange 和 GetScrollPos 函数是“陈旧的”。这是不完全准确的。虽然这着函数自从 Windows 1.0 就有了,但是它们被更新为在 Win32 API 中处理 32 位参数。它们仍然是极好的功能并且有希望保留功能。而且它们是足够简单的而不被开始的 Windows 程序设计的新来者淹没,这就是我在本书中继续使用它们的原因。
The two scroll bar functions introduced in the Win32 API are called SetScrollInfo and GetScrollInfo. These functions do everything the earlier functions do and add two new important features.
在 Win32 API 中介绍的两个滚动条函数叫做 SetScrollInfo 和 GetScrollInfo。这些函数做一些早前的函数做的事并且增加了两个新的重要的特征。
The first feature involves the size of the scroll bar thumb. As you may have noticed, the size of the thumb was constant in the SYSMETS2 program. However, in some Windows applications you may have used, the size of the thumb is proportional to the amount of the document displayed in the window. This displayed amount is known as the "page size." In arithmetic terms,
第一个特征包含滚动条滑快的尺寸。正如你可能注意到的,在 SYSMETS2 程序中滑块的尺寸是常量。然而,在你可能用过的一些 Windows 应用程序中,滑块的尺寸是与窗口中显示的文档数量成比例的。这个显示的数量称作“页尺寸”。用数学的术语来说就是
Thumb size
Page size
Amount of document displayed
=
=
Scroll length
Range
Total size of document
You can use SetScrollInfo to set the page size (and hence the size of the thumb), as we'll see in the SYSMETS3 program coming up shortly.
你可以用 SetScrollInfo 设置页尺寸(和滑块尺寸),正如我们将在不久后出现的 SYSMETS3 程序中看到的。
The GetScrollInfo function adds a second important feature, or rather it corrects a deficiency in the current API. Suppose you want to use a range that is 65,536 or more units. Back in the days of 16-bit Windows, this was not possible. In Win32, of course, the functions are defined as accepting 32-bit arguments, and indeed they do. (Keep in mind that if you do use a range this large, the number of actual physical positions of the thumb is still limited by the pixel size of the scroll bar.) However, when you get a WM_VSCROLL or WM_HSCROLL message with a notification code of SB_THUMBTRACK or SB_THUMBPOSITION, only 16 bits are provided to indicate the current position of the thumb. The GetScrollInfo function lets you obtain the actual 32-bit value.
GetScrollInfo 函数增加了第二个重要的特征,或者它改正了当前 API 中的不足。假设你想用 65,536 或更多单位的范围。回到 16 位 Windows 的时代,这是不可能的。当然,在 Win32 中这些函数被定义为接受 32 位参数,并且它们的确是这样做的。(记住:如果你用比这个大的范围,那么滑块的实际物理位置的数字仍然被滚动条的像素尺寸限制。)然而,当你获得有 SB_THUMBTRACK 或 SB_THUMBPOSITION 的通知编码的 WM_VSCROLL 或 WM_HSCROLL 消息时,只有 16 位被提供以指出滑块的当前位置。GetScrollInfo 函数允许你获得实际的 32 位值。
The syntax of the SetScrollInfo and GetScrollInfo functions is
SetScrollInfo 和 GetScrollInfo 函数的语法是
SetScrollInfo (hwnd, iBar, &si, bRedraw) ;
GetScrollInfo (hwnd, iBar, &si) ;
The iBar argument is either SB_VERT or SB_HORZ, as in the other scroll bar functions. As with those functions also, it can be SB_CTL for a scroll bar control. The last argument for SetScrollInfo can be TRUE or FALSE to indicate if you want Windows to redraw the scroll bar taking into account the new information.
The third argument to both functions is a SCROLLINFO structure, which is defined like so:
iBar 参数是 SB_VERT 或 SB_HORZ,和在其它滚动条函数中一样。同样和那些函数一样,它可以是用于滚动条控制的 SB_CTL。SetScrollInfo 的最后一个参数可以是 TRUE 或 FALSE 以指出你是否希望 Windows 重新绘制取得新信息的滚动条。这两个函数的第三个参数是 SCROLLINFO 结构,它是这样定义的:
typedef struct tagSCROLLINFO
{
UINT cbSize ; // set to sizeof (SCROLLINFO) 设为 sizeof(SCROLLINFO)
UINT fMask ; // values to set or get 设置或获得的值
int nMin ; // minimum range value 最小范围值
int nMax ; // maximum range value 最大范围值
UINT nPage ; // page size 页尺寸
int nPos ; // current position 当前位置
int nTrackPos ; // current tracking position 当前跟踪位置
}
SCROLLINFO, * PSCROLLINFO ;
In your program, you can define a structure of type SCROLLINFO like this:
在你的程序中,你可以像这样定义 SCROLLINFO 类型的结构:
SCROLLINFO si ;
Before calling SetScrollInfo or GetScrollInfo, you must set the cbSize field to the size of the structure:
在调用 SetScrollInfo 或 GetScrollInfo 之前,你必须将 cbSize 域设置为结构的大小:
si.cbSize = sizeof (si) ;
or
或者
si.cbSize = sizeof (SCROLLINFO) ;
As you get acquainted with Windows, you'll find several other structures that have a first field like this one to indicate the size of the structure. This field allows for a future version of Windows to expand the structure and add new features while still being compatible with previously compiled programs.
当你开始知道 Windows 时,你将发现几个其它结构,它们的第一个域像这个一样指出结构的大小。这个域允许未来版本的 Windows 扩展该结构并增加新的特征,而仍然与先前已编译的程序兼容。
You set the fMask field to one or more flags beginning with the SIF prefix. You can combine these flags with the C bitwise OR function (|).
你设置 fMask 域为一个或多个以 SIF 前缀开始的标志。你可以用 C 位或操作符(|)组合这些标志。
When you use the SIF_RANGE flag with the SetScrollInfo function, you must set the nMin and nMax fields to the desired scroll bar range. When you use the SIF_RANGE flag with the GetScrollInfo function, the nMin and nMax fields will be set to the current range on return from the function.
当你和 SetScrollInfo 函数一起用 SIF_RANGE 标志时,你必须设置 nMin 和 nMax 为期望的滚动条范围。当你和 GetScrollInfo 函数一起用 SIF_RANGE 时,nMin 和 nMax 域将被设置为由该函数返回的当前范围。
The SIF_POS flag is similar. When used with the SetScrollInfo function, you must set the nPos field of the structure to the desired position. You use the SIF_POS flag with GetScrollInfo to obtain the current position.
SIF_POS 标志是类似的。当和 SetScrollInfo 函数一起使用时,你必须设置该结构的 nPos 域为期望的位置。你和 GetScrollInfo 一起使用 SIF_POS 以获得当前位置。
The SIF_PAGE flag lets you set and obtain the page size. You set nPage to the desired page size with the SetScrollInfo function. GetScrollInfo with the SIF_PAGE flag lets you obtain the current page size. Don't use this flag if you don't want a proportional scroll bar thumb.
SIF_PAGE 标志允许你设置和获得页大小。你用 SetScrollInfo 函数设置 nPage 为期望的页大小。有 SIF_PAGE 标志的 GetScrollInfo 允许你获得当前的页大小。如果你不想要一个成比例的滚动条滑块,那么不要使用这个标志。
You use the SIF_TRACKPOS flag only with GetScrollInfo while processing a WM_VSCROLL or WM_HSCROLL message with a notification code of SB_THUMBTRACK or SB_THUMBPOSITION. On return from the function, the nTrackPos field of the SCROLLINFO structure will indicate the current 32-bit thumb position.
当处理有 SB_THUMBTRACK 或 SB_THUMBPOSITION 通知编码的 WM_VSCROLL 或 WM_HSCROLL 消息时,你只和 GetScrollInfo 一起使用 SIF_TRACKPOS。在由该函数返回时,SCROLLINFO 结构的 nTrackPos 域将指出当前 32 位的画块位置。
You use the SIF_DISABLENOSCROLL flag only with the SetScrollInfo function. If this flag is specified and the new scroll bar arguments would normally render the scroll bar invisible, this scroll renders the scroll bar disabled instead. (I'll explain this more shortly.)
你只和 SetScrollInfo 函数一起使用 SIF_DISABLENOSCROLL 标志。如果这个标志被指定并且新的滚动条参数通常将致使滚动条不可见,那么该控制将致使滚动条可见。(我将在不久后更多解释它。)
The SIF_ALL flag is a combination of SIF_RANGE, SIF_POS, SIF_PAGE, and SIF_TRACKPOS. This is handy when setting the scroll bar arguments during a WM_SIZE message. (The SIF_TRACKPOS flag is ignored when specified in a SetScrollInfo function.) It's also handy when processing a scroll bar message.
SIF_ALL 标志是 SIF_RANGE,SIF_POS,SIF_PAGE 和 SIF_TRACKPOS 的组合。在 WM_SIZE 消息期间设置滚动条参数是很方便的。(SIF_TRACKPOS 标志当在 SetScrollInfo 函数中被指定时被忽略。)当处理滚动条消息时它也是方便的。
How Low Can You Scroll?你可以滚动多低?
In SYSMETS2, the scrolling range is set to a minimum of 0 and a maximum of NUMLINES - 1. When the scroll bar position is 0, the first line of information is at the top of the client area; when the scroll bar position is NUMLINES - 1, the last line is at the top of the client area and no other lines are visible.
在 SYSMETS2 中,滚动范围被设置为最小 0 和最大 NUMLINES-1。当滚动条位置是 0 时,第一行信息在客户区顶端;当滚动条位置是 NUMLINES-1 时,最后一行在客户区顶端并且没有其它行是可见的。
You could say that SYSMETS2 scrolls too far. It really only needs to scroll far enough so that the last line of information appears at the bottom of the client area rather than at the top. We could make some changes to SYSMETS2 to accomplish this. Rather than set the scroll bar range when we process the WM_CREATE message, we could wait until we receive the WM_SIZE message:
你可以说 SYSMETS2 滚动得太远了。它实际上只需要滚动到足够最后一行信息出现在客户区底端而不是顶端。我们将对 SYSMETS2 做一些改变以完成它。当我们处理 WM_CREATE 消息时,没有设置滚动条范围,我们可以等到我们接收到 WM_SIZE 消息时:
iVscrollMax = max (0, NUMLINES - cyClient / cyChar) ;
SetScrollRange (hwnd, SB_VERT, 0, iVscrollMax, TRUE) ;
Suppose NUMLINES equals 75, and suppose for a particular window size that cyClient divided by cyChar equals 50. In other words, we have 75 lines of information but only 50 can fit in the client area at any time. Using the two lines of code shown above, the range is set to a minimum of 0 and a maximum of 25. When the scroll bar position equals 0, the program displays lines 0 through 49. When the scroll bar position equals 1, the program displays lines 1 through 50; and when the scroll bar position equals 25 (the maximum), the program displays lines 25 through 74. Obviously we'd have to make changes to other parts of the program, but this is entirely doable.
假设 NUMLINES 等于 75,并且假设 cyClinet/cyChar 的特殊窗口尺寸等于 50。换句话说,我们有 75 行而不仅仅是 50 行信息可以在任何时候容纳在客户区中。用上面显示的两行代码,范围被设置为最小 0 和最大 25。当滚动条位置等于 0 时,程序显示 0 到 49 行。当滚动条位置等于 1 时,程序显示 1 到 50 行;而当滚动条位置等于 25(最大值)时,程序显示 25 到 74 行。明显地我们将不得不改变程序的其它部分,但是这是完全可行的。
One nice feature of the new scroll bar functions is that when you use a scroll bar page size, much of this logic is done for you. Using the SCROLLINFO structure and SetScrollInfo, you'd have code that looked something like this:
新滚动条函数的一个好的功能是当你使用滚动条页大小时,这个逻辑的大部分不需要你做。使用 SCROLLINFO 结构和 SetScrollInfo,你将编写看起来有些像这样的代码:
si.cbSize = sizeof (SCROLLINFO) ;
si.cbMask = SIF_RANGE | SIF_PAGE ;
si.nMin = 0 ;
si.nMax = NUMLINES - 1 ;
si.nPage = cyClient / cyChar ;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
When you do this, Windows limits the maximum scroll bar position not to si.nMax but to si.nMax - si.nPage + 1. Let's make the same assumptions as earlier: NUMLINES equals 75 (so si.nMax equals 74), and si.nPage equals 50. This means that the maximum scroll bar position is limited to 74 - 50 + 1, or 25. This is exactly what we want.
当你这样做时,Windows 限制滚动条的最大位置不是 si.nMax 而是 si.nMax-si.nPage+1 。让我们做一个和先前一样的假定:NUMLINES 等于 75(因此 si.nMax 等于 74),而 si.nPage 等于 50。这意味着滚动条的最大位置被限制为 74-50+1 或 25。这确实是我们想要的。
What happens when the page size is as large as the scroll bar range? That is, in this example, what if nPage is 75 or above? Windows conveniently hides the scroll bar because it's no longer needed. If you don't want the scroll bar to be hidden, use SIF_DISABLENOSCROLL when calling SetScrollInfo and Windows will merely disable the scroll bar rather than hide it.
当页大小和滚动条范围一样大时会发生什么呢?在这个例子中也就是说如果 nPage 是 75 或更大会发生什么呢?Windows 方便地隐藏滚动条,因为它不再需要。如果你不想滚动条被隐藏,那么在调用 SetScrollInfo 时用 SIF_DISABLENOSCROLL,Windows 仅仅使滚动条不可用而不是隐藏它。
The New SYSMETS新 SYSMETS
SYSMETS3—our final version of the SYSMETS program in this chapter—is shown in Figure 4-11. This version uses the SetScrollInfo and GetScrollInfo functions, adds a horizontal scroll bar for left and right scrolling, and repaints the client area more efficiently.
SYSMETS3——在本章中我们的 SYSMETS 程序的最终版本——在图 4-11 中显示。这个版本使用 SetScrollInfo 和 GetScrollInfo 函数,增加了用于左和右滚动的水平滚动条,并且更高效地重画客户区。
Figure 4-11. The SYSMETS3 program.
图 4-11 SYSMETS3 程序
SYSMETS3.C
/*----------------------------------------------------
SYSMETS3.C -- System Metrics Display Program No. 3
(c) Charles Petzold, 1998
----------------------------------------------------*/
#include <windows.h>
#include "sysmets.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("SysMets3") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 3"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth ;
HDC hdc ;
int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;
PAINTSTRUCT ps ;
SCROLLINFO si ;
TCHAR szBuffer[10] ;
TEXTMETRIC tm ;
switch (message)
{
case WM_CREATE:
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
cyChar = tm.tmHeight + tm.tmExternalLeading ;
ReleaseDC (hwnd, hdc) ;
// Save the width of the three columns
iMaxWidth = 40 * cxChar + 22 * cxCaps ;
return 0 ;
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
// Set vertical scroll bar range and page size
si.cbSize = sizeof (si) ;
si.fMask = SIF_RANGE | SIF_PAGE ;
si.nMin = 0 ;
si.nMax = NUMLINES - 1 ;
si.nPage = cyClient / cyChar ;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
// Set horizontal scroll bar range and page size
si.cbSize = sizeof (si) ;
si.fMask = SIF_RANGE | SIF_PAGE ;
si.nMin = 0 ;
si.nMax = 2 + iMaxWidth / cxChar ;
si.nPage = cxClient / cxChar ;
SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
return 0 ;
case WM_VSCROLL:
// Get all the vertical scroll bar information
si.cbSize = sizeof (si) ;
si.fMask = SIF_ALL ;
GetScrollInfo (hwnd, SB_VERT, &si) ;
// Save the position for comparison later on
iVertPos = si.nPos ;
switch (LOWORD (wParam))
{
case SB_TOP:
si.nPos = si.nMin ;
break ;
case SB_BOTTOM:
si.nPos = si.nMax ;
break ;
case SB_LINEUP:
si.nPos -= 1 ;
break ;
case SB_LINEDOWN:
si.nPos += 1 ;
break ;
case SB_PAGEUP:
si.nPos -= si.nPage ;
break ;
case SB_PAGEDOWN:
si.nPos += si.nPage ;
break ;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos ;
break ;
default:
break ;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS ;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
GetScrollInfo (hwnd, SB_VERT, &si) ;
// If the position has changed, scroll the window and update it
if (si.nPos != iVertPos)
{
ScrollWindow (hwnd, 0, cyChar * (iVertPos - si.nPos),
NULL, NULL) ;
UpdateWindow (hwnd) ;
}
return 0 ;
case WM_HSCROLL:
// Get all the vertical scroll bar information
si.cbSize = sizeof (si) ;
si.fMask = SIF_ALL ;
// Save the position for comparison later on
GetScrollInfo (hwnd, SB_HORZ, &si) ;
iHorzPos = si.nPos ;
switch (LOWORD (wParam))
{
case SB_LINELEFT:
si.nPos -= 1 ;
break ;
case SB_LINERIGHT:
si.nPos += 1 ;
break ;
case SB_PAGELEFT:
si.nPos -= si.nPage ;
break ;
case SB_PAGERIGHT:
si.nPos += si.nPage ;
break ;
case SB_THUMBPOSITION:
si.nPos = si.nTrackPos ;
break ;
default :
break ;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS ;
SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
GetScrollInfo (hwnd, SB_HORZ, &si) ;
// If the position has changed, scroll the window
if (si.nPos != iHorzPos)
{
ScrollWindow (hwnd, cxChar * (iHorzPos - si.nPos), 0,
NULL, NULL) ;
}
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
// Get vertical scroll bar position
si.cbSize = sizeof (si) ;
si.fMask = SIF_POS ;
GetScrollInfo (hwnd, SB_VERT, &si) ;
iVertPos = si.nPos ;
// Get horizontal scroll bar position
GetScrollInfo (hwnd, SB_HORZ, &si) ;
iHorzPos = si.nPos ;
// Find painting limits
iPaintBeg = max (0, iVertPos + ps.rcPaint.top / cyChar) ;
iPaintEnd = min (NUMLINES - 1,iVertPos + ps.rcPaint.bottom / cyChar) ;
for (i = iPaintBeg ; i <= iPaintEnd ; i++)
{
x = cxChar * (1 - iHorzPos) ;
y = cyChar * (i - iVertPos) ;
TextOut (hdc, x, y,
sysmetrics[i].szLabel,
lstrlen (sysmetrics[i].szLabel)) ;
TextOut (hdc, x + 22 * cxCaps, y,
sysmetrics[i].szDesc,
lstrlen (sysmetrics[i].szDesc)) ;
SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
TextOut (hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer,
wsprintf (szBuffer, TEXT ("%5d"),
GetSystemMetrics (sysmetrics[i].iIndex))) ;
SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
This version of the program relies on Windows to maintain the scroll bar information and do a lot of the bounds checking. At the beginning of WM_VSCROLL and WM_HSCROLL processing, it obtains all the scroll bar information, adjusts the position based on the notification code, and then sets the position by calling SetScrollInfo. The program then calls GetScrollInfo. If the position was out of range in the SetScrollInfo call, the position is corrected by Windows and the correct value is returned in the GetScrollInfo call.
这个版本的程序依赖于 Windows 维护滚动条信息并且做了许多范围检查。在 WM_VSCROLL 和 WM_HSCROLL 处理的开始,它获得滚动条的所有信息,基于通知编码调整位置,然后通过调用 SetScrollInfo 设置位置。然后程序调用 GetScrollInfo。如果位置超出 SetScrollInfo 调用中的范围,那么位置由 Windows 更正并且正确的值在 GetScrollInfo 调用中返回。
SYSMETS3 uses the ScrollWindow function to scroll information in the window's client area rather than repaint it. Although the function is rather complex (and has been superseded in recent versions of Windows by the even more complex ScrollWindowEx), SYSMETS3 uses it in a fairly simple way. The second argument to the function gives an amount to scroll the client area horizontally in pixels, and the third argument is an amount to scroll the client area vertically.
SYSMETS3 用 ScrollWindow 函数滚动窗口客户区中的信息而不是重画它。尽管这个函数更复杂(并且在最近版本的 Windows 中已经被甚至更复杂的 ScrollWindowEx 取代),但是 SYSMETS3 以一种非常简单的方式使用它。该函数的第二个参数给出了以像素为单位水平地滚动客户区的数量,而第三个参数是垂直地滚动客户区的数量。
The last two arguments to ScrollWindow are set to NULL. This indicates that the entire client area is to be scrolled. Windows automatically invalidates the rectangle in the client area "uncovered" by the scrolling operation. This generates a WM_PAINT message. InvalidateRect is no longer needed. Note that ScrollWindow is not a GDI function because it does not require a handle to a device context. It is one of the few non-GDI Windows functions that changes the appearance of the client area of a window. Rather peculiarly but conveniently, it is documented along with the scroll bar functions.
ScrollWindow 的最后两个参数被设置为 NULL。这指出整个客户区都被滚动。Windows 通过滚动操作自动指出客户区中“未覆盖的”矩形。这产生 WM_PAINT 消息。InvalidateRect 不再需要。注意 ScrollWindow 不是 GDI 函数,因为它不要求关联设备句柄。它是新的改变窗口客户区外观的非 GDI Windows 函数之一。不特别但是方便地,它和滚动条函数一起备有文档说明。
The WM_HSCROLL processing traps the SB_THUMBPOSITION notification code and ignores SB_THUMBTRACK. Thus, if the user drags the thumb on the horizontal scroll bar, the program will not scroll the contents of the window horizontally until the user releases the mouse button.
WM_HSCROLL 处理捕获 SB_THUMBPOSITION 通知编码并忽略 SB_THUMBTRACK。因此,如果用户拖动水平滚动条上的滑块,那么程序将直到用户释放鼠标按键时才水平地滚动窗口的内容。
The WM_VSCROLL strategy is different: here, the program traps SB_THUMBTRACK messages and ignores SB_THUMBPOSITION. Thus, the program scrolls its contents vertically in direct response to the user dragging the thumb on the vertical scroll bar. This is considered preferable, but watch out: It is well known that when users find out a program scrolls in direct response to dragging the scroll bar thumb, they will frenetically jerk the thumb back and forth trying to bring the program to its knees. Fortunately, today's fast PCs are much more likely to survive this torture test. But try your code out on a slow machine, and perhaps think about using the SB_SLOWMACHINE argument to GetSystemMetrics for alternative processing for slow machines.
WM_VSCROLL 策略是不同的:这里,程序捕获 SB_THUMBTRACK 消息并忽略 SB_THUMBPOSITION。因此,程序垂直地滚动它的内容以直接响应用户拖动垂直滚动条上的画块。这是考虑过更好的,但是当心:众所周知当用户发现程序滚动直接响应拖动滚动条滑块时,他们将狂热地拉回滑块并且尝试强制将程序返回它原来的地方。幸运地是,今天的更快的 PC 能更好的幸免于这种痛苦的测试。但是在缓慢的机器上尝试你的代码,并且也许认为用 SB_SLOWMACHINE 参数于 GetSystemMetrics 以用于缓慢的机器选择处理。
One way to speed up WM_PAINT processing is illustrated by SYSMETS3: The WM_PAINT code determines which lines are within the invalid rectangle and rewrites only those lines. The code is more complex, of course, but it is faster.
一种加速 WM_PAINT 处理的方法是由 SYSMETS3 图解的:WM_PAINT 代码确定那一行在无效矩形中并且只重画那些行。当然这段代码更复杂,但是它更快。
But I Don't Like to Use the Mouse但是我不喜欢使用鼠标
In the early days of Windows, a significant number of users didn't care for using the mouse, and indeed, Windows itself (and many Windows programs) did not require a mouse. Although mouseless PCs have now generally gone the way of monochrome displays and dot-matrix printers, it is still recommended that you write programs that duplicate mouse operations with the keyboard. This is particularly true for something as fundamental as scroll bars, because our keyboards have a whole array of cursor movement keys that should offer alternatives to the mouse.
在早期的 Windows 中,大量的重要用户不愿意使用鼠标,而且的确,Windows 本身(和许多 Windows)不需要鼠标。尽管没有鼠标的 PC 现在通常已经走上了单色显示器和点阵式打印机的道路,但是我仍然建议你编写用键盘复制鼠标操作的程序。这对于一些和滚动条一样基础的东西是特别正确的,因为我们的键盘有所有光标移动键的排列,它应该提供相对于鼠标的可选择的东西。
In the next chapter, you'll learn how to use the keyboard and how to add a keyboard interface to this program. You'll notice that SYSMETS3 seems to process WM_VSCROLL messages when the notification code equals SB_TOP and SB_BOTTOM. I mentioned earlier that a window procedure doesn't receive these messages for scroll bars, so right now this is superfluous code. When we come back to this program in the next chapter, you'll see the reason for including those operations.
在下一章中,你将学会如何使用键盘和如何为该程序增加键盘接口。你将注意到 SYSMETS3 在通知编码等于 SB_TOP 和 SB_BOTTOM 时似乎处理 WM_VSCROLL 消息。我早先提到窗口处理函数不接收这些滚动条的消息,因此目前这是多余的代码。当我们在下一章中回到这个程序时,你将看到包含那些操作的原因。