|
range 范围
在应用程序的窗口中包括滚动条相当容易。只需要在CreateWindows的第三个参数中包括窗口风格标识符WS_VSCROLL (垂直滚动条) 或WS_HSCROLL(水平滚动条)或者同时包括两者。客户区并不包含滚动条所占的空间。
滚动条的范围和位置
滚动条的范围是一对整数,分别代表滚动条的最小值和最大值。
位置:指滑块在范围中所处的值。当滑块在滚动条的顶端(或最左)时,滑块的位置是范围的最小值。
相应的,当滑块在滚动条的底部(或最右)时,位置是范围的最大值。
SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ;
iBar参数 要么是SB_VERT 要么是SB_HORZ
iMin、iMax 分别对应范围的最小值 、最大值
需要Windows根据新的范围来重绘滚动条时,请将bReadraw设为TRUE
( 如果在调用SetScrollRange函数之后还将调用其他函数来调整滚动条的显示时,最好将bReadraw设为FALSE以避免过多的重绘)。
通过 SetScrollPos 函数用来指定滑块在滚动条范围中的位置:
SetScrollPos( hwnd, iBar, iPos, bRearaw ) ;
参数iPos是滑块的新位置,它必须在iMin和iMax之间。
Windows提供了两个类似的函数(GetScrollRange)和(GetScrollPos)用于获取滚动条的当前范围和位置。
在程序中使用滚动条时,程序需要和Windows共同负责维护滚动条以及滑块在滚动条中的位置。
程序需要处理的任务:
1.初始化滚动条的范围和位置。
2.处理传送给窗口过程的滚动条消息。
3.更新滑块的位置
4.根据滚动条的变化更新客户区的内容
滚动条消息
wParam参数被分为低位字和高位字。
wParam的低位字代表了鼠标在滚动条上的动作。这个值被称为“通知码”,由一个SB开头的标识符定义(SB代表滚动条)。
下面是在WINUSER.H中定义的通知码:
/*
* Scroll Bar Commands
*/
#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
上面显示了鼠标在滚动条的不同位置单击时相应的通知码。
如果在滚动条的不同部分按住鼠标键不放,程序可能收到多条滚动条消息。当松开鼠标键时,程序会收到一条带有SB_ENDSCROLL通知码的消息。
程序通常可以忽略带SB_ENDSCROLL的消息。Windows并不会自己改变滑块的位置,应用程序需要调用SetScrollPos函数来改变它。
将鼠标放在滑块上然后按下鼠标键时,可以移动滑块。这将会生成带SB_THUMBTRACK 和 SB_THUMBPOSITION通知码的滚动条消息。
当wParam的低位字是SB_THUMBTRACK时,wParam的高位字是用户拖动滑块的当前位置。这个位置处于滚动条范围的最小值和最大值之间
当wParam的低位字是SB_THUMBPOSITION 时,wParam的高位字是用户松开鼠标键时滑块的最终位置。
对于其他的滚动条动作,wParam的高位字应被忽略。
WinMain函数中的处理:
hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL, //WS_VSCROLL 表示垂直滚动条
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
窗口过程中的处理:
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) ;//设置滑块的初始位置,但是iVscrollPos没有赋值啊???不懂系统自动处理?
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)) ;//确保新滑块的位置在滚动条范围之内
if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))//获取滑块位置与当前位置比较。
{
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ; //设置新滑块的位置
InvalidateRect (hwnd, NULL, TRUE) ; //使窗口无效重绘,WINDOWS发送一条WM_PAINT消息
} //可以立即调用UpdateWindow(hwnd); 因为WM_PAINT优先级比较低
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
for (i = 0 ; i < NUMLINES ; i++)
{
y = cyChar * (i - iVscrollPos) ; //上翻 或下翻的行数
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) ;
}
运行结果:

改进的滚动条:
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static int cxChar, cxCaps, cyChar, cyClient, iVscrollPos,iVscrollMax;
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); // lParam高位字代表客户区的高度
iVscrollMax = max(0, NUMLINES-cyClient/cyChar+1); //注:窗口的客户区发生改变 cyClient / cyChar值的改变
SetScrollRange(hwnd,SB_VERT,0,iVscrollMax,TRUE);
return 0;
case WM_VSCROLL:
switch(LOWORD(wParam)) // 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-cyClient/cyChar+1));
if(iVscrollPos != GetScrollPos(hwnd,SB_VERT))
{
SetScrollPos(hwnd,SB_VERT,iVscrollPos,TRUE);
InvalidateRect(hwnd,NULL,TRUE);
// UpdateWindow(hwnd); //立刻更新无效区域
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd,&ps);
for (i = 0; i < NUMLINES; i++)
{
y = cyChar*(i-iVscrollPos);
TextOut(hdc,0,y,
sysmetrics[i].szLabel,
strlen(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);
}
运行结果:

效果更好的滚动条
SetScrollRang 、 GetScrollRang 、 GetScrollPos 、 GetScrollPos 用法简单,滑块的大小固定,自WINDOWS 1.0 就已经存在。 下面将使用WIN32中独有的一些新函数: SetScrollInfo 和 GetScrollInfo 这两个函数有上面的所有功能,还加入了两个重要的新功能。
第一个新功能就是关于滑块的大小。

另一重要的功能是改进了滚动条范围上的不足,可以得到实际的32位值
SetScrollInfo (hwnd, iBar, &si, bRedraw);
GetScrollInfo (hwnd, iBar, &si);
参数iBar 除了有SB_VERT 或 SB_HORZ。它也可以是SB_CTL,表示一个滚动条控件
typedef struct tagSCROLLINFO {
UINT cbSize; //设为sizeof(SCROLLINFO)
UINT fMask; //要设置或获取的值
int nMin; //范围的最小值
int nMax; //范围的最大值
UINT nPage; //页面大小
int nPos; //当前位置
int nTrackPos; //当前追踪位置
} SCROLLINFO, *LPSCROLLINFO;
typedef SCROLLINFO CONST *LPCSCROLLINFO;
注意 在调用这两个函数之前,必须将cbSize字段设为该结构的大小;
si.cbSize = sizeof(si); 或 si.cbSize = sizeof(SCROLLINFO);
fMask字段是一个或多个以SIF为前缀的标志。它们可以用“位或”运算组合在一起。
在SetScrollInfo中指定了SIF_RANGE时,必须在nMin 和nMax中指定滚动条的范围。
在GetScrollInfo中指定了SIF_RANGE时,nMin 和nMax将返回当前的范围。
SIF_POS与:此类似;
SIF_PANG :可用于指定或获取页面大小。
在SetScrollInfo中你在nPage中指定页面大小,而GetScrollInfo在nPage中返回页面大小。如果不希望滑块大小发生变化,就不要使用这个标志。
自适应滑块大小垂直滚动条:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxChar, cxCaps, cyChar, cyClient;
HDC hdc ;
int i, y ,iVertPos,iPaintBeg,iPaintEnd;
PAINTSTRUCT ps ;
SCROLLINFO si;
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);
return 0 ;
case WM_SIZE:
cyClient = HIWORD (lParam) ;
si.fMask = SIF_RANGE|SIF_PAGE;
si.cbSize = sizeof(si);
si.nMin = 0;
si.nMax = NUMLINES - 1;
si.nPage = cyClient / cyChar;
SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
return 0 ;
case WM_VSCROLL:
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&si);
iVertPos = si.nPos;
switch (LOWORD (wParam))
{
case SB_TOP:
si.nPos = si.nMin;
return 0;
case SB_BOTTOM:
si.nPos = si.nMax;
return 0;
case SB_LINEUP:
si.nPos -= 1 ;
break ;
case SB_LINEDOWN:
si.nPos += 1 ;
break ;
case SB_PAGEUP:
si.nPos -= si.nPage ;
break ;
case SB_PAGEDOWN:
si.nPos += si.nPage ;
break ;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos ;
break ;
default :
break ;
}
si.fMask = SIF_POS;
SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
GetScrollInfo(hwnd,SB_VERT,&si);
if(si.nPos != iVertPos)
{
ScrollWindow(hwnd,0,cyChar * (iVertPos - si.nPos),NULL,NULL);
UpdateWindow(hwnd);
}
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo(hwnd,SB_VERT,&si);
iVertPos = si.nPos;
iPaintBeg = max(0,iVertPos + ps.rcPaint.top /cyChar);
iPaintEnd = min(NUMLINES - 1,iVertPos + ps.rcPaint.bottom / cyChar); //滑块位置相对 无效区域的大小???
for (i = iPaintBeg ; i <= iPaintEnd ; i++)
{
y = cyChar * (i - iVertPos) ;
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) ;
}
运行结果:

这个版本的滚动条不用担心滚动的太多了。也不需要自己调整。但是还有一些细节没有弄明白...
理解以后再添加注释 |