/*---------------------------------------------------- SYSMETS3.C -- System Metrics Display Program No. 3 (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 ("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 vertial 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 vertial 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) ; }
这个程序相比之前,加入了水平滚动条和在拖动过程中客户区的变化,所以也就多了一些变量和对消息的处理。
我们先来看下 SCROLLINFO 这个结构体:
typedef struct tagSCROLLINFO { UINT cbSize ; // set to 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,*LPSCROLLINFO;
为什么要引进这个结构体呢?
相对于 SetScrollRange(), SetScrollPos(), GetScrollRange(), GetScrollPos() 函数,
SetScrollInfo(), GetScrollInfo() 这两个函数更先进,因为它含有上述四个函数的所有功能,还加了两个新功能。
第一个功能是关于滑块的大小。滑块的大小应该和窗口文档的多少成比例。为了美观可以看如下的公式:
滑块大小 / 滚动条长度 = 页面大小 / 范围 = 文档显示的数量 / 文档的总大小
这两个函数的语法如下:
SetScrollRange(hwnd, iBar, &si, bRedraw);
GetScrollRange(hwnd,iBar,&si);
可以看到有个 &si 这个东西。其实它就是 ScrollInfo结构体的变量。程序通常将该结构体变量这样命名。
同样的 iBar 也只能是 SB_VERT, SB_HORZ, SB_CTL 中的一个。SB_CTL 表示一个滚动条控件。
在调用这两个函数之前,必须将 cbSize 字段设为该结构体的大小:
si.cbSize = sizeof(si);
或者 si.cbSize = sizeof(SCROLLINFO);
可能很多老哥们都想到既然是个定值,那为啥要给第一个字段呢?
因为这样,以后的 Windows 版本可以扩展结构而同时保持与以前的应用程序兼容。
fMask 字段是一个或多个以 SIF 为前缀的标志。它们可以用 "位或" 运算组合在一起。
fMask: 指定结构中的哪些成员是有效,该值共有如下5种选择,可以选择多种用“OR”组合起来,该值在设置和查询参数时都必须填写。
SIF_ALL :整个结构都有效
SIF_DISABLENOSCROLL:该值仅在设定参数时使用,视控件参数设定的需要来对本结构的成员进行取舍。
SIF_PAGE :nPage成员有效
SIF_POS :nPos成员有效
SIF_RANGE :nMin和nMax成员有效
nMin:滚动范围最小值
nMax:滚动范围最大值
nPage:页尺寸,用来确定比例滚动框的大小
nPos:滚动框的位置
nTrackPos:拖动时滚动框的位置,该参数只能查询,不能设置。
在 SetScrollInfo() 中指定了 SIF_RANGE 时,必须在 nMin 和 nMax 中指定滚动条的范围。
SIF_POS 时,必须在 nPos 字段指定滚动条的位置。
SIF_PAGE 时,必须在 nPage 字段指定页面的大小。
SIF_DISABLENOSCROLL, 作用原来让滚动条不显示的设置这时将禁用滚动条。( 该函数独有 )
在 GetScrollInfo() 中,指定了标志的,就返回相应的值。
SIF_TRACKPOS 标志(该函数独有),而且只在处理通知码是 SB_THUMBTRACK 或
SB_THUMBPOSITION 的 WM_VSCROLL 或 WM_HSCROLL 消息时。
在函数返回时,SCROLLINFO结构的 nTrackPos 字段将返回当前滑块的位置(32位整数)。
这里我们依赖 Windows 来维护滚动条信息和做边界检查。在处理 WM_VSCROLL 和 WM_HSCROLL 时
首先需获取滚动条消息,根据通知码调整位置,然后调用 SetScrollInfo() 函数设置位置。程序然后调用
GetScrollInfo()。如果在调用 SetScrollInof() 时超出了范围,Windows 将自动修正位置,并通过 GetScrollInfo()
调用返回正确的位置。
ScrollWindow() 函数:
该函数滚动所指定的窗口客户区域内容。
BOOL ScrollWindow(HWND hWnd, int XAmount, int YAmount, CONST RECT *IpRect, CONST RECT *lpClipRect);
XAmount 指定水平滚动的距离,YAmount 指定垂直滚动的距离。
lpRect 指向 RECT 结构的指针,该结构指定了将要滚动的客户区范围。若此参数为NULL,则整个客户区域将被滚动。
lpClipRect 指向 RECT 结构的指针,该结构指定了要滚动的裁剪区域。只有这个矩形中的位才会被滚动。
在矩形之外的位不会被影响,即使它们是在lpRect矩形之内。假如lpClipRect为NULL,则不会在滚动矩形上进行裁剪。