由于显示的字符可能会不全,我们很容易想到的一个解决办法是使用滚动条。
先看一下代码,再进行分析:
/*----------------------------------------------------
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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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[] ;
TEXTMETRIC tm ; switch (message)
{
case WM_CREATE:
hdc = GetDC (hwnd) ; GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cxCaps = (tm.tmPitchAndFamily & ? : ) * cxChar / ;
cyChar = tm.tmHeight + tm.tmExternalLeading ; ReleaseDC (hwnd, hdc) ; SetScrollRange (hwnd, SB_VERT, , NUMLINES - , FALSE) ;
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
return ; case WM_SIZE:
cyClient = HIWORD (lParam) ;
return ; case WM_VSCROLL:
switch (LOWORD (wParam))
{
case SB_LINEUP:
iVscrollPos -= ;
break ; case SB_LINEDOWN:
iVscrollPos += ;
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 (, min (iVscrollPos, NUMLINES - )) ; if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
{
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
InvalidateRect (hwnd, NULL, TRUE) ;
}
return ; case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ; for (i = ; i < NUMLINES ; i++)
{
y = cyChar * (i - iVscrollPos) ; TextOut (hdc, , y,
sysmetrics[i].szLabel,
lstrlen (sysmetrics[i].szLabel)) ; TextOut (hdc, * cxCaps, y,
sysmetrics[i].szDesc,
lstrlen (sysmetrics[i].szDesc)) ; SetTextAlign (hdc, TA_RIGHT | TA_TOP) ; TextOut (hdc, * cxCaps + * cxChar, y, szBuffer,
wsprintf (szBuffer, TEXT ("%5d"),
GetSystemMetrics (sysmetrics[i].iIndex))) ; SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
}
EndPaint (hwnd, &ps) ;
return ; case WM_DESTROY:
PostQuitMessage () ;
return ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
我们在CreateWindow函数的第三个参数加上 WS_VSCROLL 即垂直滚动条。
同时在WM_CREATE中加入:
SetScrollRange (hwnd, SB_VERT, , NUMLINES - , FALSE) ;
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
SetScrollRange设置滚动条范围,NUMLINES是在头文件中定义的行数,
滚动条的每个位置对应一行文字。
iVscrollPos变量记录这滑块的当前位置。
在鼠标拖动滑块时,会产生WM_VSCROLL消息,对iVscrollPos变量进行赋值操作。
还有更改的一处是Y坐标: y = cyChar * (i - iVscrollPos) ; 这保证了滑动之后,小于当前位置的字符不会输出到屏幕(因为他们Y坐标是负的)
同时在滑动滚动条的时候:
if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
{
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
InvalidateRect (hwnd, NULL, TRUE) ;
}
当滚动条位置改变之后我们会用InvalidateRect函数使窗口无效,会生成WM_PAINT消息,重绘窗口。