显示区域大小:
我们曾经使用过 GetClientRect 函数来获取显示区域的大小,使用这个函数没有什么不好,只是效率太低,确定显示区域更好的方法是在窗口消息处理程序中处理 WM_SIZE消息。传递给窗口消息处理程序的lParam 参数的低字节包含显示区域的宽度,高字节包含高度。
static int nxClient, nyClient;
case WM_SIZE:
nxClient = LOWORD(lParam);
nyClient = HIWORD(lParam);
return 0;
滚动条的范围和位置:
每个滚动条有一个相关的“范围”和“位置”,位置是一个整数序列。
SetScrollRange(hwnd, iBar, iMin, iMax, bRedraw);
// iBar 为 SB_VERT 或者 SB_HORZ。如果想要 Windows 根据新范围重画滚动条,则 //设置bRedraw 为 TRUE;
SetScrollPos(hwnd, iBar, iPos, bRedraw);
//iPos 是设置在iMin 和 iMax 之间的整数值
当释放鼠标按键后,程序会收到一个带有SB_ENDSCROLL通知码的消息,一般可以忽略这个消息。
当把鼠标的光标在滑块上按鼠标时,就会产生SB_THUMBTRACK 和 SB_THUMBPOSITION 通知码的滚动条消息。当 wParam 的低字节是 SB_THUMBTRACK时,wParam 的高字节是使用者在拖动滑块时的当前位置。当 wParam 的低字节是 SB_THUMBPOSITION 时,wParam 的高字节是使用者释放滑块的最终位置。
滚动条范围使用 32 位的值也是可以的,但是当使用了32位的时候,wParam(16位) 的高字节就不能准确获得滑块当前的位置了,这时候,需要使用 GetScrollInfo 函数来得到信息。
OK,来写代码:
第一步,在 CreateWindow 中添加 WS_VSCROLL 如下:
hwnd = CreateWindow(szClsName, TEXT("Scroll Test."), WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
第二步,添加 WM_VSCROLL 响应:
WM_VSCROLL:
switch(LOWORD){wParam}
{
case SB_THUMBTRACK:
nVPos = HIWORD(wParam);
break;
case SB_PAGEDOWN:
nVPos += nyClient / nyChar;
break;
case SB_PAGEUP:
nVPos -= nyClinet / nyChar;
break;
case SB_LINEDOWN:
nVPos += 1;
break;
case SB_LINEUP:
nVPos -= 1;
break;
}
nVPos = max(0, min(nVPos, NUMLINES - nyClinet / nyChar));
if(nVPos != GetScrollPos(hwnd, SB_VERT)) //滑块位置改变
{
SetScrollPos(hwnd, SB_VERT, nVPos, TRUE); //重设位置
InvalidateRect(hwnd, NULL, TRUE); //重绘显示区
}
第三步:添加绘制响应
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for(i = 0; i < NUMLINES; i++)
{
y = nyChar * (i - nVPos);
TextOut(hdc, 0, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel));
TextOut(hdc, 22*nxCaps, y,
sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, 22*nxCaps + 40*nxChar, y,
szBuf, wsprintf(szBuf, TEXT("%d"), GetSystemMetrics(sysmetrics[i].Index)));
SetTextAlign(hdc, TA_LEFT | TA_TOP);
}
EndPaint(hwnd, &ps);
第四步,当窗口大小发生改变时重绘
case WM_SIZE:
nyClient = HIWORD(lParam);
SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - nyClient / nyChar, FALSE);
SetScrollPos(hwnd, SB_VERT, nVPos, TRUE);
if((NUMLINES - nVPos)*nyChar < nyClient && NUMLINES * nyChar > nyClient)
{
nVPos = NUMLINES - nyClient / nyChar;
//PostMessage(hwnd, WM_VSCROLL, (nVPos << 16) & SB_THUMBTRACK, 0);
}
/// 源码 /
#define UNICODE UNICODE #include <Windows.h> #include "metrics.h"
LRESULT CALLBACK WndProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { HDC hdc; PAINTSTRUCT ps; static int nxChar, nyChar, nxCaps; static int nyClient, nVPos; int i, y; TCHAR szBuf[10];
switch(uMsg) { case WM_CREATE: TEXTMETRIC tm; hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm);
nxChar = tm.tmAveCharWidth; nyChar = tm.tmHeight + tm.tmExternalLeading; nxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * nxChar / 2 ; ReleaseDC(hwnd, hdc);
case WM_PAINT: hdc = BeginPaint(hwnd, &ps); for(i = 0; i < NUMLINES; i++) { y = nyChar * (i - nVPos); TextOut(hdc, 0, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel)); TextOut(hdc, 22*nxCaps, y, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc, TA_RIGHT | TA_TOP); TextOut(hdc, 22*nxCaps + 40*nxChar, y, szBuf, wsprintf(szBuf, TEXT("%d"), GetSystemMetrics(sysmetrics[i].Index))); SetTextAlign(hdc, TA_LEFT | TA_TOP); } EndPaint(hwnd, &ps); return 0; case WM_SIZE: nyClient = HIWORD(lParam); SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - nyClient / nyChar, FALSE); SetScrollPos(hwnd, SB_VERT, nVPos, TRUE); if((NUMLINES - nVPos)*nyChar < nyClient && NUMLINES * nyChar > nyClient) { nVPos = NUMLINES - nyClient / nyChar; //PostMessage(hwnd, WM_VSCROLL, (nVPos<<16) & SB_THUMBTRACK,0); } return 0; case WM_VSCROLL: switch(LOWORD(wParam)) { case SB_THUMBTRACK: nVPos = HIWORD(wParam); break; case SB_PAGEDOWN: nVPos += nyClient / nyChar; break; case SB_PAGEUP: nVPos -= nyClient / nyChar; break; case SB_LINEDOWN: nVPos += 1; break; case SB_LINEUP: nVPos -= 1; break; } nVPos = max(0, min(nVPos, NUMLINES - nyClient / nyChar)); if(nVPos != GetScrollPos(hwnd, SB_VERT)) { SetScrollPos(hwnd, SB_VERT, nVPos, TRUE); InvalidateRect(hwnd, NULL, TRUE); } return 0; case WM_CLOSE: DestroyWindow(hwnd); case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { static TCHAR szClsName[] = TEXT("HelloWin"); HWND hwnd; MSG msg; WNDCLASS cls; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); cls.hCursor = LoadCursor(NULL, IDC_ARROW); cls.hIcon = LoadIcon(NULL, IDI_APPLICATION); cls.hInstance = hInstance; cls.lpfnWndProc = WndProc; cls.lpszClassName = szClsName; cls.lpszMenuName = NULL; cls.style = CS_VREDRAW | CS_HREDRAW; if(!RegisterClass(&cls)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szClsName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szClsName, TEXT("Bing's Pad"), WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }