一、滚动条的建立:
在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) ;
![]()
}
![]()
如果仔细看了这些代码,会发现很多是一种经验的积累,它的处理方式清晰简洁,也是学习的一种方法呢!