Chapter 4 An Exercise in Text Output 第四章 文本输出练习 — Scroll Bars - 滚动条

类别:编程语言 点击:0 评论:0 推荐:
ps:您可以转载,但请注明出处;你可以修改,但请将修改结果告诉我。
  Scroll Bars 滚动条   Scroll bars are one of the best features of a graphical user interface. They are easy to use and provide excellent visual feedback. You can use scroll bars whenever you need to display anything—text, graphics, a spreadsheet, database records, pictures, Web pages—that requires more space than is available in the window's client area. 滚动条是图形用户界面最好的特征之一。它们容易使用并提供极好的视觉反馈。你可以在你需要显示任何东西——文本,图形,电子表格,数据库记录,图片,网页——的任何(要求比窗口的客户区可用空间更多的空间)时候使用滚动条。   Scroll bars are positioned either vertically (for up and down movement) or horizontally (for left and right movement). You can click with the mouse the arrows at each end of a scroll bar or the area between the arrows. A "scroll box" (or "thumb") travels the length of the scroll bar to indicate the approximate location of the material shown on the display in relation to the entire document. You can also drag the thumb with the mouse to move to a particular location. Figure 4-7 shows the recommended use of a vertical scroll bar for text. 滚动条可以垂直地(用于上和下移动)或水平地(用于左和右移动)放置。你可以用鼠标点击滚动条两端的箭头或箭头之间的区域。“滚动块”(或“滑块”)移动滚动条的长度以指出显示器上相对于整个文档显示的材料的大约位置。你也可以用鼠标拖动滑块以移动到特殊的位置。图 4-7 显示用于文本的垂直滚动条的推荐用法。   Figure 4-7. The vertical scroll bar. 图 4-7 垂直滚动条   Programmers sometimes have problems with scrolling terminology because their perspective is different from the user's. A user who scrolls down wants to bring a lower part of the document into view; however, the program actually moves the document up in relation to the display window. The Window documentation and the header file identifiers are based on the user's perspective: scroll up means moving toward the beginning of the document; scroll down means moving toward the end. 程序员有时候有关于滚动术语的问题,因为他们的角度和用户的不同。向下滚动的用户想把文档下面的部分带入视线;然而,程序实际上将文档相对于显示器窗口上移。Window 文档和头文件标识符是基于用户的观点:向上滚动意味着朝文档的开始部分移动;向下滚动意味着朝文档的结尾部分移动。   It is easy to include a horizontal or vertical scroll bar in your application window. All you need do is include the window style (WS) identifier WS_VSCROLL (vertical scroll) or WS_HSCROLL (horizontal scroll) or both in the third argument to CreateWindow. The scroll bars specified in the CreateWindow function are always placed against the right side or bottom of the window and extend the full length or width of the client area. The client area does not include the space occupied by the scroll bar. The width of the vertical scroll bar and the height of the horizontal scroll bar are constant for a particular video driver and display resolution. If you need these values, you can obtain them (as you may have observed) from the GetSystemMetrics calls. 在你的应用程序窗口中包含水平或垂直滚动条是容易的。你所需要做的是在 CreateWindow 的第三个参数中包括窗口风格(WS)标识符 WS_VSCROLL(垂直滚动)或 WS_HSCROLL(水平滚动)或两者都有。在 CreateWindow 中指定的滚动条总是放置在窗口的右边或底端并延伸到客户区的整个长度或宽度。客户区不包括由滚动条占据的空间。垂直滚动条的宽度和水平滚动条的高度对于特殊的视频驱动程序和显示分辨率来说是常量。如果你需要这些值,你可以从 GetSystemMetrics 调用获得它们(正如你可能已经观察到的)。   Windows takes care of processing all mouse messages to the scroll bars. However, scroll bars do not have an automatic keyboard interface. If you want the cursor keys to duplicate some of the functionality of the scroll bars, you must explicitly provide logic for that (as we'll do when we make another version of the SYSMETS program in the next chapter). Windows 照顾所有到滚动条的鼠标消息的处理。然而,滚动条没有自动的键盘界面。如果你希望光标键复制一些滚动条的功能,那么你必须显式地为它们提供逻辑(正如当我们在下一章中编写另一个版本的 SYSMETS 时我们将要做的)。   Scroll Bar Range and Position 滚动条范围和位置   Every scroll bar has an associated "range" and "position." The scroll bar range is a pair of integers representing a minimum and maximum value associated with the scroll bar. The position is the location of the thumb within the range. When the thumb is at the top (or left) of the scroll bar, the position of the thumb is the minimum value of the range. At the bottom (or right) of the scroll bar, the thumb position is the maximum value of the range. 每一个滚动条都有一个关联的“范围”和“位置”。滚动条的范围是一对代表与滚动条相关的最小和最大值。位置是这个范围内滑块的位置。当滑块在滚动条的顶端(或左端)时,滑块的位置是该范围的最小值。在滚动条的底端(或右端)时,滑块位置是该范围的最大值。   By default, the range of a scroll bar is 0 (top or left) through 100 (bottom or right), but it's easy to change the range to something that is more convenient for the program: 缺省情况下,滚动条的范围是 0(顶端或左端)到 100(底端或右端),将这个范围改变为更便于程序使用的东西是容易的:   SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ;   The iBar argument is either SB_VERT or SB_HORZ, iMin and iMax are the new minimum and maximum positions of the range, and you set bRedraw to TRUE if you want Windows to redraw the scroll bar based on the new range. (If you will be calling other functions that affect the appearance of the scroll bar after you call SetScrollRange, you'll probably want to set bRedraw to FALSE to avoid excessive redrawing.) iBar 参数是 SB_VERT 或 SB_HORZ,iMin 和 iMax 是该范围的新的最小和最大位置,并且如果你希望 Windows 重画基于新范围的滚动条,那么你将 bRedraw 设置为 TRUE。(如果在你调用 SetScrollRange 之后你将调用其它影响滚动条外观的函数,那么你将可能希望将 bRedraw 设置为 FALSE 以避免过多的重画。)   The thumb position is always a discrete integral value. For instance, a scroll bar with a range of 0 through 4 has five thumb positions, as shown in Figure 4-8. 滑块位置总是一个离散的整数值。例如,范围从 0 到 4 的滚动条有五个滑块位置,如图 4-8 中显示。   Figure 4-8. Scroll bars with five thumb positions. 图 4-8 有五个滑块位置的滚动条   You can use SetScrollPos to set a new thumb position within the scroll bar range: 你可以用 SetScrollPos 在滚动条范围内设置新的滑块位置:   SetScrollPos (hwnd, iBar, iPos, bRedraw) ;   The iPos argument is the new position and must be within the range of iMin and iMax. Windows provides similar functions (GetScrollRange and GetScrollPos) to obtain the current range and position of a scroll bar. iPos 参数是新位置并且必须在 iMin 和 iMax 的范围内。Windows 提供类似的函数(GetScrollRange 和 GetScrollPos)以获得滚动条的当前范围和位置。   When you use scroll bars within your program, you share responsibility with Windows for maintaining the scroll bars and updating the position of the scroll bar thumb. These are Windows' responsibilities for scroll bars: 当你在你的程序内使用滚动条时,你要和 Windows 一起负责维护滚动条和更新滚动条滑块的位置。这些是 Windows 对滚动条的责任: Handle all processing of mouse messages to the scroll bar. 操纵所有到滚动条的鼠标消息的处理。 Provide a reverse-video "flash" when the user clicks the scroll bar. 当用户点击滚动条时提供反转视频“闪烁”。 Move the thumb as the user drags the thumb within the scroll bar. 当用户在滚动条内拖动滑块时移动滑块。 Send scroll bar messages to the window procedure of the window containing the scroll bar. 发送滚动条消息给包含滚动条的窗口的窗口处理函数。 These are the responsibilities of your program: 这些是你的程序的责任: Initialize the range and position of the scroll bar. 初始化滚动条的范围和位置。 Process the scroll bar messages to the window procedure. 处理到窗口处理函数的滚动条消息。 Update the position of the scroll bar thumb. 更新滚动条滑块的位置。 Change the contents of the client area in response to a change in the scroll bar. 改变客户区的内容以响应滚动条的改变。  Like almost everything in life, this will make a lot more sense when we start looking at some code. 像生活中几乎每一件事一样,当我们开始看一些代码时这将有更多感触。   Scroll Bar Messages 滚动条消息   Windows sends the window procedure WM_VSCROLL (vertical scroll) and WM_HSCROLL (horizontal scroll) messages when the scroll bar is clicked with the mouse or the thumb is dragged. Each mouse action on the scroll bar generates at least two messages, one when the mouse button is pressed and another when it is released. 当滚动条被用鼠标点击或滑块被拖动时,Windows 发送给窗口处理函数 WM_VSCROLL(垂直滚动)和 WM_HSCROLL(水平滚动)消息。每一个滚动条上的鼠标动作都产生至少两个消息,一个在鼠标按键被压下时而另一个在它被释放时。   Like all messages, WM_VSCROLL and WM_HSCROLL are accompanied by the wParam and lParam message parameters. For messages from scroll bars created as part of your window, you can ignore lParam; that's used only for scroll bars created as child windows, usually within dialog boxes. 像所有的消息一样,WM_VSCROLL 和 WM_HSCROLL 由 wParam 和 lParam 消息参数伴随。对于来自创建为你的窗口的一部分的滚动条的消息来说,你可以忽略 lParam;它只用于创建为子窗口的滚动条,通常在对话框内。   The wParam message parameter is divided into a low word and a high word. The low word of wParam is a number that indicates what the mouse is doing to the scroll bar. This number is referred to as a "notification code." Notification codes have values defined by identifiers that begin with SB, which stands for "scroll bar." Here's how the notification codes are defined in WINUSER.H: wParam 消息参数被分成低位字和高位字。wParam 的低位字是指出鼠标正在对滚动条做什么的数字。这个数字被看作是一个“通知编码”。通知编码有由 SB(它代表“scroll bar”)开始的标识符定义的值。这里是这些通知编码在 WINUSER.H 中是如何定义的:   #define SB_LINEUP 0 #define SB_LINELEFT 0 #define SB_LINEDOWN 1 #define SB_LINERIGHT 1 #define SB_PAGEUP 2 #define SB_PAGELEFT 2 #define SB_PAGEDOWN 3 #define SB_PAGERIGHT 3 #define SB_THUMBPOSITION 4 #define SB_THUMBTRACK 5 #define SB_TOP 6 #define SB_LEFT 6 #define SB_BOTTOM 7 #define SB_RIGHT 7 #define SB_ENDSCROLL 8   You use the identifiers containing the words LEFT and RIGHT for horizontal scroll bars, and the identifiers with UP, DOWN, TOP, and BOTTOM with vertical scroll bars. The notification codes associated with clicking the mouse on various areas of the scroll bar are shown in Figure 4-9. 你将包含单词 LEFT 和 RIGHT 的标识符用于水平滚动条,而有 UP,DOWN,TOP 和 BOTTOM 的标识符用于垂直滚动条。与在滚动条的不同区域上点击鼠标相关的通知编码在图 4-9 中显示。   Figure 4-9. Identifiers for the wParam values of scroll bar messages. 图 4-9 滚动条消息的 wParam 值的标识符   If you hold down the mouse button on the various parts of the scroll bar, your program can receive multiple scroll bar messages. When the mouse button is released, you'll get a message with a notification code of SB_ENDSCROLL. You can generally ignore messages with the SB_ENDSCROLL notification code. Windows will not change the position of the scroll bar thumb. Your application does that by calling SetScrollPos. 如果你在滚动条的各个部分压住鼠标按键,那么你的程序可以接收到多个滚动条消息。当鼠标按键释放时,你将获得有 SB_ENDSCROLL 通知编码的消息。Windows 将不会改变滚动条滑块的位置。你的应用程序通过调用 SetScrollPos 这样做。   When you position the mouse cursor over the scroll bar thumb and press the mouse button, you can move the thumb. This generates scroll bar messages with notification codes of SB_THUMBTRACK and SB_THUMBPOSITION. When the low word of wParam is SB_THUMBTRACK, the high word of wParam is the current position of the scroll bar thumb as the user is dragging it. This position is within the minimum and maximum values of the scroll bar range. When the low word of wParam is SB_THUMBPOSITION, the high word of wParam is the final position of the scroll bar thumb when the user released the mouse button. For other scroll bar actions, the high word of wParam should be ignored. 当你将鼠标指针放置在滚动条滑块上并压下鼠标按键时,你可以移动滑块。这产生有 SB_THUMBTRACK 和 SB_THUMBPOSITION 的通知编码的滚动条消息。当 wParam 的低位字是 SB_THUMBTRACK 时,wParam 的高位字是当用户正在拖动滚动条滑块时它的当前位置。这个位置是在滚动条范围的最小和最大值内。当 wParam 的低位字是 SB_THUMBPOSITION 时,wParam 的高位字是当用户释放鼠标按键时滚动条滑块的最终位置。对于其它滚动条动作,wParam 的高位字应该被忽略。   To provide feedback to the user, Windows will move the scroll bar thumb when you drag it with the mouse as your program is receiving SB_THUMBTRACK messages. However, unless you process SB_THUMBTRACK or SB_THUMBPOSITION messages by calling SetScrollPos, the thumb will snap back to its original position when the user releases the mouse button. 为了提供反馈给用户,Windows 将在你用鼠标拖动滚动条滑块时移动它,同时你的程序接收到 SB_THUMBTRACK 消息。然而,除非你通过 SetScrollPos 处理 SB_THUMTRACK 或 SB_THUMPOSITION 消息,否则当用户释放鼠标按键时滑块将迅速跳回它的最初位置。   A program can process either the SB_THUMBTRACK or SB_THUMBPOSITION messages, but doesn't usually process both. If you process SB_THUMBTRACK messages, you'll move the contents of your client area as the user is dragging the thumb. If instead you process SB_THUMBPOSITION messages, you'll move the contents of the client area only when the user stops dragging the thumb. It's preferable (but more difficult) to process SB_THUMBTRACK messages; for some types of data your program may have a hard time keeping up with the messages. 程序可以处理 SB_THUMBTRACK 或 SB_THUMBPOSITION 消息,但是通常不处理它们。如果你处理 SB_THUMBTRACK 消息,那么你将在用户拖动滑块时移动你的客户区的内容。如果你处理 SB_THUMBPOSITION 消息,那么你将只在用户停止拖动滑块时移动客户区的内容。处理 SB_THUMBTRACK 消息是更好的(但也更难);对于一些类型的数据你的程序可能很难有时间跟上这些消息。   As you'll note, the WINUSER.H header files includes notification codes of SB_TOP, SB_BOTTOM, SB_LEFT, and SB_RIGHT, indicating that the scroll bar has been moved to its minimum or maximum position. However, you will never receive these notification codes for a scroll bar created as part of your application window. 正如你将注意到的,WINUSER.H 头文件包含指出滚动条已经被移动到它的最小或最大位置的通知编码 SB_TOP,SB_BOTTOM,SB_LEFT 和 SB_RIGHT。然而,对于创建为你的应用程序窗口一部分的控制条,你将不会接收到这些通知编码。   Although it's not common, using 32-bit values for the scroll bar range is perfectly valid. However, the high word of wParam, which is only a 16-bit value, cannot properly indicate the position for SB_THUMBTRACK and SB_THUMBPOSITION actions. In this case, you need to use the function GetScrollInfo (described later in this chapter) to get this information. 尽管它不常用,但是将 32 位值用于滚动条范围是更有效的。然而,只是 16 位值的 wParam 的高位字不可以适当地指出 SB_THUMBTRACK 和 SB_THUMBPOSITION 动作的位置。在这种情况下,你需要用函数 GetScrollInfo(在本章后面描述)获得这个信息。   Scrolling SYSMETS 滚动 SYSMETS   Enough explanation. It's time to put this stuff into practice. Let's start simply. We'll begin with vertical scrolling because that's what we desperately need. The horizontal scrolling can wait. SYSMET2 is shown in Figure 4-10. This program is probably the simplest implementation of a scroll bar you'll want in an application. 解释得足够了。是将该材料放进实践中的时候了。让我们简单地开始。我们将用垂直滚动条开始,因为那是我们最需要的。水平滚动条可以等等。SYSMETS2 在图 4-10 中显示。这个程序可能是最简单的你将在应用程序中使用的滚动条的实现。   Figure 4-10. The SYSMETS2 program. 图 4-10 SYSMETS2 程序

SYSMETS2.C

/*---------------------------------------------------- SYSMETS2.C -- System Metrics Display Program No. 2 (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 ("SysMets2") ; 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 ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"), WS_OVERLAPPEDWINDOW | WS_VSCROLL, 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, cyClient, iVscrollPos ; HDC hdc ; int i, y ; PAINTSTRUCT ps ; 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) ; SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ; SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ; return 0 ; case WM_SIZE: cyClient = HIWORD (lParam) ; return 0 ; case WM_VSCROLL: switch (LOWORD (wParam)) { case SB_LINEUP: iVscrollPos -= 1 ; break ; case SB_LINEDOWN: iVscrollPos += 1 ; break ; case SB_PAGEUP: iVscrollPos -= cyClient / cyChar ; break ; case SB_PAGEDOWN: iVscrollPos += cyClient / cyChar ; break ; case SB_THUMBPOSITION: iVscrollPos = HIWORD (wParam) ; break ; default : break ; } iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ; if (iVscrollPos != GetScrollPos (hwnd, SB_VERT)) { SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ; InvalidateRect (hwnd, NULL, TRUE) ; } return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; for (i = 0 ; i < NUMLINES ; i++) { y = cyChar * (i - iVscrollPos) ; TextOut (hdc, 0, y, sysmetrics[i].szLabel, lstrlen (sysmetrics[i].szLabel)) ; TextOut (hdc, 22 * cxCaps, y, sysmetrics[i].szDesc, lstrlen (sysmetrics[i].szDesc)) ; SetTextAlign (hdc, TA_RIGHT | TA_TOP) ; TextOut (hdc, 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) ; } The new CreateWindow call adds a vertical scroll bar to the window by including the WS_VSCROLL window style in the third argument: 新 CreateWindow 调用通过在第三个参数中包含 WS_VSCROLL 窗口风格增加垂直滚动条给窗口:   WS_OVERLAPPEDWINDOW | WS_VSCROLL   WM_CREATE message processing in the WndProc window procedure has two additional lines to set the range and initial position of the vertical scroll bar: 在 WndProc 窗口处理函数中的 WM_CREATE 消息处理有两个设置垂直滚动条范围和初始位置的额外的行:   SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ; SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;   The sysmetrics structure array has NUMLINES lines of text, so the scroll bar range is set to 0 through NUMLINES - 1. Each position of the scroll bar corresponds to a line of text displayed at the top of the client area. If the scroll bar thumb is at position 0, the first line will be positioned at the top of the client area. For positions greater than zero, other lines appear at the top. When the position is NUMLINES - 1, the last line of text appears at the top of the client area. sysmetrics 结构数组有 NUMLINES 行文本,因此滚动条范围被设置为 0 到 NUMLINES-1。每个对应一行文本的滚动条位置显示在客户区顶端。如果滚动条滑块是在位置 0,那么第一行将位于客户区顶端。对于比零更大的位置,其它行出现在顶端。当位置是 NUMLINES-1 时,最后一行文本出现在客户区顶端。   To help with processing of the WM_VSCROLL messages, a static variable named iVscrollPos is defined within the window procedure. This variable is the current position of the scroll bar thumb. For SB_LINEUP and SB_LINEDOWN, all we need to do is adjust the scroll position by 1. For SB_PAGEUP and SB_PAGEDOWN, we want to move the text by the context of one screen, or cyClient divided by cyChar. For SB_THUMBPOSITION, the new thumb position is the high word of wParam. The SB_ENDSCROLL and SB_THUMBTRACK messages are ignored. 为了有助于处理 WM_VSCROLL 消息,叫做 iVscrollPos 的静态变量被定义在窗口处理函数内。这个变量是滚动条滑块的当前位置。对于 SB_LINEUP 和 SB_LINEDOWN,我们所需要做的是调整滚动条位置 1。对于 SB_PAGEUP 和 SB_PAGEDOWN,我们向按一屏内容或 cyClient/cyChar 移动文本。对于 SB_THUMBPOSITION,新的滑块位置是 wParam 的高位字。SB_ENDSCROLL 和 SB_THUMBPOSITION 消息被忽略。   After the program calculates a new value of iVscrollPos based on the type of WM_VSCROLL message it receives, it makes sure that it is still between the minimum and maximum range value of the scroll bar by using the min and max macros. The program then compares the value of iVscrollPos with the previous position, which is obtained by calling GetScrollPos. If the scroll position has changed, it is updated by calling SetScrollPos, and the entire window is invalidated by a call to InvalidateRect. 在程序计算基于它接收到的 WM_VSCROLL 消息类型的新的 iVscrollPos 值之后,它通过使用 min 和 max 确保它仍然在滚动条的最小和最大范围值之间。然后程序用通过调用 GetScrollPos 获得的前一个位置比较 iVscrollPos 值。如果滚动条位置改变了,那么它通过调用 SetScrollPos 更新并且整个窗口通过调用 InvalidateRect 是无效的。   The InvalidateRect function generates a WM_PAINT message. When the original SYSMETS1 program processed WM_PAINT messages, the y-coordinate of each line was calculated as InvalidateRect 函数产生 WM_PAINT 消息。当最初的 SYSMETS1 程序处理 WM_PAINT 消息时,每行的 y 坐标通过   cyChar * i   In SYSMETS2, the formula is 计算。在 SYSMETS2 中,公式是   cyChar * (i - iVscrollPos)   The loop still displays NUMLINES lines of text, but for nonzero values of iVscrollPos this value is negative. The program is actually displaying the early lines of text above and outside the client area. Windows, of course, doesn't allow these lines to appear on the screen, so everything looks all nice and neat. 这个循环仍然显示 NUMLINES 行文本,但是对于非零值的 iVscrollPos 这个值是负数。程序实际上显示前面行的文本在客户区上面和外面。当然,Windows 不允许这些行出现在屏幕上,因此每一样看起来都很好和整齐。   I told you we'd start simply. This is rather wasteful and inefficient code. We'll fix it shortly, but first consider how we update the client area after a WM_VSCROLL message. 我告诉我你我将简单地开始。这是更浪费和低效率的代码。我将在不久修正它,但是首先考虑我们如何在 WM_VSCROLL 消息之后更新客户区。   Structuring Your Program for Painting 组织你的绘图程序   The window procedure in SYSMETS2 does not directly repaint the client area after processing a scroll bar message. Instead, it calls InvalidateRect to invalidate the client area. This causes Windows to place a WM_PAINT message in the message queue.  SYSMETS2 中的窗口处理函数在处理滚动条消息之后不直接重绘客户区。代替的是,它调用 InvalidateRect 使客户区无效。这使得 Windows 将 WM_PAINT 消息放置在消息队列中。   It is best to structure your Windows programs so that you do all your client-area painting in response to a WM_PAINT message. Because your program should be able to repaint the entire client area of the window at any time on receipt of a WM_PAINT message, painting in response to other messages will probably involve code that duplicates the functionality of your WM_PAINT logic. 最好组织你的 Windows 程序以便你做响应 WM_PAINT 消息的所有客户区绘制。因为你的程序应该可以在任何接收到 WM_PAINT 消息时重画窗口的整个客户区,所以响应其它消息的绘制将可能包含复制你的 WM_PAINT 逻辑的功能的代码。   At first, you may rebel at this dictum because it seems such a roundabout way of doing things. In the early days of Windows, programmers found this concept difficult to master because it was so different from character-mode PC programming. And, as I mentioned earlier, there are frequently times when your program will respond to some keyboard or mouse logic by drawing something immediately. This is done for both convenience and efficiency. But in many cases it's simply unnecessary. After you master the discipline of accumulating all the information you need to paint in response to a WM_PAINT message, you'll be pleased with the results. 起先,你可能厌恶这个规则,因为它似乎是迂回的做事方法。在早期的 Windows 中,程序员发现这个概念难于掌握,因为它是如此不同于字符模式的 PC 程序设计。而且,正如我早前提到的,当你的程序将通过立即绘制一些东西响应一些键盘或鼠标逻辑时,它会很频繁。这样做是方便和高效的。但是在许多情况下它完全是不必要的。在你掌握计算你响应 WM_PAINT 消息绘制需要的所有信息的训练之后,你将对这个结果满意。   As SYSMETS2 demonstrates, a program will often determine that it must repaint a particular area of the display while processing a message other than WM_PAINT. This is where InvalidateRect comes in handy. You can use it to invalidate specific rectangles of the client area or the entire client area. 正如 SYSMETS2 演示的,程序将常常确定在处理非 WM_PAINT 消息时它必须重画显示器的特殊区域。这就是 InvalidateRect 有用的地方。你可以用它使客户区的指定矩形或整个客户区无效。     Simply marking areas of the window as invalid to generate WM_PAINT messages might not be entirely satisfactory in some applications. After you make an InvalidateRect call, Windows places a WM_PAINT message in the message queue and the window procedure eventually processes it. However, Windows treats WM_PAINT messages as low priority, so if a lot of other activity is occurring in the system, it may be awhile before your window procedure receives the WM_PAINT message. Everyone has seen blank, white "holes" in Windows after a dialog box is removed and the program is still waiting to refresh its window. 简单地将窗口区域标志为无效以产生 WM_PAINT 消息可能在有些应用程序中不是完全令人满意的。在你调用 InvalidateRect 之后,Windows 将 WM_PAINT 消息放置在消息队列中并且窗口处理函数最终处理它。然而,Windows 把 WM_PAINT 消息看作是低优先级的,因此如果许多其它行为正在系统中发生,那么它可能在你的窗口处理函数接收到 WM_PAINT 消息之前一会儿。每个人都曾看到过在对话框被删除 Windows 中空的,白的“洞”,而程序仍然在等待刷新它的窗口。   If you prefer to update the invalid area immediately, you can call UpdateWindow after you call InvalidateRect: 如果你更喜欢立即更新无效区域,那么你可以在调用 InvalidateRect 之后调用 UpdateWindow:   UpdateWindow (hwnd) ;   UpdateWindow causes the window procedure to be called immediately with a WM_PAINT message if any part of the client area is invalid. (UpdateWindow will not call the window procedure if the entire client area is valid.) In this case, the WM_PAINT message bypasses the message queue. The window procedure is called directly from Windows. When the window procedure has finished repainting, it exits and the UpdateWindow function returns control to the code that called it. 如果客户区的任何部分是无效的,那么 UpdateWindow 使得窗口处理函数立即被用 WM_PAINT 消息调用。(如果整个客户区是有效的,那么 UpdateWindow 将不调用窗口处理函数。)在这种情况下,WM_PAINT 消息迂回到消息队列。窗口处理函数直接由 Windows 调用。当窗口处理函数完成重画时,它存在并且 UpdateWindow 函数返回控制给调用它的代码。   You'll note that UpdateWindow is the same function used in WinMain to generate the first WM_PAINT message. When a window is first created, the entire client area is invalid. UpdateWindow directs the window procedure to paint it. 你将注意到 UpdateWindow 是和在 WinMain 中用来产生第一个 WM_PAINT 消息相同的函数。当窗口首先被创建时,整个客户区是无效的。UpdateWindow 命令窗口处理函数绘制它。

本文地址:http://com.8s8s.com/it/it26669.htm