一、滚动条的建立:
在CreateWindow的第三个参数中包括窗口样式(WS)标识符WS_VSCROLL(垂直卷动)和/或WS_HSCROLL(水平卷动)即可。
二、需要做的:
Windows对滚动条的处理:- 处理所有滚动条鼠标事件
- 当使用者在滚动条内单击鼠标时,提供一种「反相显示」的闪烁
- 当使用者在滚动条内拖动卷动方块时,移动卷动方块
- 为包含滚动条窗口的窗口消息处理程序发送滚动条消息
程序写作者应该完成的工作:
- 初始化滚动条的范围和位置
1、范围
SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ;参数iBar为SB_VERT或者SB_HORZ,iMin和iMax分别是范围的最小值和最大值。如果想要Windows根据新范围重画滚动条,则设置bRedraw为TRUE(如果在呼叫SetScrollRange后,呼叫了影响滚动条位置的其它函数,则应该将bRedraw设定为FALSE以避免过多的重画)。
一般情况下,最小值和最大值应该是与文本紧密关联的内容,以方便操作。
2、位置SetScrollPos (hwnd, iBar, iPos, bRedraw) ;参数iPos是新位置,它必须在iMin至iMax的范围内。Windows提供了类似的函数(GetScrollRange和GetScrollPos)来取得滚动条的目前范围和位置。
- 处理窗口消息处理程序的滚动条消息
在用鼠标单击滚动条或者拖动卷动方块时,Windows给窗口消息处理程序发送WM_VSCROLL(供上下移动)和WM_HSCROLL(供左右移动)消息。在滚动条上的每个鼠标动作都至少产生两个消息,一条在按下鼠标按钮时产生,一条在释放按钮时产生。
和所有的消息一样,WM_VSCROLL和WM_HSCROLL也带有wParam和lParam消息参数。wParam消息参数被分为一个低字组和一个高字组。wParam的低字组是一个数值,它指出了鼠标对滚动条进行的操作。
这些操作有:
#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
在wParam的低字组是SB_THUMBTRACK时,wParam的高字组是使用者在拖动卷动方块时的目前位置。
PS:取得低字组的方法是使用LOWORD(wParam),取得高字组的方法是使用HIWORD(wParam)。
- 更新滚动条内卷动方块的位置
Windows不会去改变卷动方块的位置,而您可以在程序中呼叫SetScrollPos来改变卷动方块的位置。
- 更改显示区域的内容以响应对滚动条的更改(看注释)
/*------------------------------------------------------------------
SYSMETS2.C -- System Metrics Display Program No. 2
(c) Charles Petzold, 1998
------------------------------------------------------------------*/
#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)) ;//非常精彩的宏!控制了iVscrollPos不会超过ScrollRange
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) ;//iVscrollPos中记录了偏移的大小,所以y需要重新计算
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) ;
}
如果仔细看了这些代码,会发现很多是一种经验的积累,它的处理方式清晰简洁,也是学习的一种方法呢!