显示区域大小:
我们曾经使用过 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;
}