同样头文件还是原来的头文件。
源文件:
/*---------------------------------------------------- SYSMETS2.C -- System Metrics Display Program No. 2 (c) Charles Petzold, 1998 ----------------------------------------------------*/ #define WINVER 0x0500 #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) ; }
在应用程序中加入滚动条相当容易,只需在 CreateWindow() 函数的第三个参数中包括窗口风格标识符
WS_VSCROLL(垂直滚动条) 或 WS_HSCROLL(水平滚动条), 或者同时包括两者。
在 CreateWindow() 函数中指定的滚动条总是出现在窗口的右边和底部,而且总是伸展到整个客户区的宽度和高度。
客户区并不包含滚动条所占用的空间。
Windows负责处理滚动条中所有鼠标消息。但是,滚动条并没有自动对应的键盘接口。
如果想将方向键对应到滚动条上,则必须显示的提供相应的对应关系。
每个滚动条都有相应的"范围"和"位置"。滚动条的范围是一对整数,分别代表滚动条的最小值和最大值。
位置是指滑块在范围中所处的值。当滑块在滚动条的顶端时,滑块的位置是范围的最小值。相应的,
当滑块在滚动条的底部(或最右)时,位置是范围最大值。
在默认情况下,滚动条的范围是 1~100,不过通过下面的函数调用,可以很方便的把范围改成对程序更有意义的值:
SetScrollRange(hwnd, iBar, iMin, iMax, bRedraw);
这里的 iBar 参数要么是 SB_VERT(垂直滚动条范围), 要么是 SB_HORZ(水平滚动条范围), 用来指定滚动条将被设置。
而 iMin 和 iMax 分别对应范围的最小值和最大值。
需要 Windows 根据新的范围来重绘滚动条时,请将 bRedraw 参数设为 TRUE, (如果在调用 SetScrollRange() 函数
之后还调用其他函数来调整滚动条的显示时,最好将bRedraw 设为 FALSE 以避免过多的重绘)。
通过 SetScrollPos() 函数调用即可指定滑块在滚动条范围中的位置:
SetScrollPos(hwnd, iBar, iPos, bRedraw);
这里的参数 iPos 是滑块的新位置,他必须在 iMin 和 iMax 之间。 Windows 提供了两个类似的函数
GetScrollRange() 和 GetScrollPos() 用于获取滚动条的当前范围和位置。
另外,在程序中使用滚动条时,程序需要和 Windows 共同负责维护滚动条以及滑块在滚动条中的位置。
请记住以下几点:
Windows 负责如下任务:
- 处理滚动条中所有鼠标消息
- 当用户单击滚动条时,提供一种反向显示的闪烁
- 当用户拖动滑块时,在滚动条内移动滑块
- 向拥有滚动条的窗口的窗口过程(WndProc() )发送滚动条消息
- 初始化滚动条的范围和位置
- 处理传送给窗口过程的滚动条消息
- 更新滑块的位置
- 根据滚动条的变化更新客户区的内容
相应的 lParam 变量的低位是客户区的宽度,而高位自然就是高度。
可以这样处理 WM_SIZE 消息:
case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0;
滚动条消息:
当用户单击滚动条或拖到滚动条时,Windows 向窗口过程发送 WM_VSCROLL 消息(垂直滚动)或 WM_HSCROLL(水平滚动)。
在滚动条的上的任何鼠标动作会产生至少两条消息,一条在鼠标按下时,另一条在鼠标键松开时。
就像所有消息一样,WM_VSCROLL 和 WM_HSCROLL 消息都伴随着 wParam 和 lParam 消息参数。
当滚动条是窗口一部分时,可以忽略 lParam 参数:它只用于滚动条是子窗口时,通常是在对话框中。
同样 wParam 参数也被分为低位字和高位字。低位字代表了鼠标在滚动条上的动作。这个值被称为"通知码"。
由一个以 SB 开头的标识符定义(SB 代表滚动条)。
下面是在 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
凡是含有 LEFT, RIGHT 的标识符用于水平滚动条中,而含有 UP, DOWN, TOP, BOTTOM 的标识符用于垂直滚动条中。
当松开鼠标键时,程序会收到 SB_ENDSCROLL 消息。
将鼠标放在滑块上然后按下鼠标键时,可以移动滑块。这将会产生带 SB_THUMBPOSITION 和 SB_THUMBTRACK
通知码的滚动消息。当 wParam 的低位字是 SB_THUMBTRACK 时,它的高位字便是用户拖动滑块的当前位置。
如果低位字是 SB_THUMBPOSITION 时,它的高位字便是用户松开鼠标键时滑块的最终位置。
可以看下图加深理解:
好了,现在程序应该可以看懂了吧!
不过该程序有缺陷,因为它没有处理鼠标在拖动滑块的过程中对客户区的重绘!
下节将进一步完善。