本节讲述窗口滚动条的简单使用方法。如果窗口客户区的内容太多,为了方便浏览窗口客户区的所有内容,就需要在创建窗口时添加窗口垂直或水平滚动条样式。窗口过程处理WM_CREATE消息时初始化滚动条的位置和滚动范围。窗口过程处理WM_VSCROLL或WM_HSCROLL消息时响应滚动条的操作,改变滚动条滑块的位置。
本节必须掌握的知识点:
滚动条
第20练:简单的窗口滚动条
3.2.1 滚动条
■滚动条
在Windows系统中,滚动条(Scrollbar)是一种常见的用户界面元素,用于在有限的显示区域中查看较大的内容。滚动条通常出现在窗口的右侧和底部,用户可以通过点击或拖动滚动块(Thumb)来查看不同部分的内容。既可以用鼠标单击滚动条两端的箭头,也可以单击箭头之间的区域。一个“滑块”在滚动条中移动,表明当前显示的内容在整个文档中的大概位置。可以用鼠标拖动滑块到特定的位置。
程序员和用户看待滚动条的角度不同,当用户向下滚动时,实际是程序将文件相对于显示窗口向上移动。windows文档和头文件中的标识符都是从用户的角度出发:向上滚动意味着移向文档的开始,而向下滚动意味着移向文档的结束。
在Windows编程中,你可以通过几种方式来为窗口添加滚动条:
1.将滚动条作为窗口的一部分,在创建窗口(CreateWindow)时添加滚动条。这是本章将要实现的滚动条。
2.将滚动条作为一个独立的子窗口,添加滚动条子窗口控件。我们将在第八章讲解滚动条子窗口控件。
■滚动条的范围和位置
每个滚动条都有相应地“范围”和“位置”。滚动条的范围是一对整数,分别代表滚动条的最小值和最大值。位置是指滑块在范围中所处的值。当滑块在滚动条的顶端(或最左)时,滑块的位置是范围的最小值。相应的,当滑块在滚动条的底部(或最右)时,位置是范围的最大值。在默认状态下,滚动条的范围是0~100。
在Windows编程中,可以使用一组API函数来管理窗口滚动条的范围(即滚动条能代表的最大最小值)和位置(即滚动条滑块当前表示的值)。
1.设置滚动条范围:使用SetScrollRange函数,可以设定滚动条可以表示的最小值和最大值。
BOOL SetScrollRange(
HWND hWnd,
int nBar,
int nMinPos,
int nMaxPos,
BOOL bRedraw
);
这个函数将滚动条的最小位置值设置为nMinPos,最大位置值设置为nMaxPos。nBar参数指定设置哪一个滚动条,如果是水平滚动条,则传入SB_HORZ,垂直滚动条则传入SB_VERT。
2.获取滚动条范围:使用GetScrollRange函数,可以获取滚动条当前的最小值和最大值。
BOOL GetScrollRange(
HWND hWnd,
int nBar,
LPINT lpMinPos,
LPINT lpMaxPos
);
3.设置滚动条当前位置:使用SetScrollPos函数。
int SetScrollPos(
HWND hWnd,
int nBar,
int nPos,
BOOL bRedraw
);
这个函数将滚动条的当前位置设置为nPos。nBar参数指定设置哪一个滚动条,如果是水平滚动条,则传入SB_HORZ,垂直滚动条则传入SB_VERT。如果bRedraw参数为TRUE,则滚动条会被立即重绘。
4.获取滚动条当前位置:使用GetScrollPos函数。
int GetScrollPos(
HWND hWnd,
int nBar
);
上述涉及到的所有函数的hWnd参数都是要控制滚动条的那个窗口的句柄。
在使用滚动条时,一个常见的模式是,在响应滚动条相关的Windows消息(如WM_SCROLL)时,获取和设置滚动条的位置。滚动条的位置信息经常用于决定窗口内容的绘制,以实现在大尺寸内容上滑动查看的效果。
■Windows负责如下任务:
●处理滚动条中的所有鼠标消息。
●当使用者单击管道条时,提供一种反向显示的闪烁。
●当使用者拖动滚动块时,在滚动条内移动滚动块。
●向拥有滚动条的窗口的窗口过程发送滚动条消息。
■程序需要负责的任务:
●初始化滚动条的范围和位置 。
●处理传送给窗口过程的滚动条消息 。
●更新滚动块的位置 。
●根据滚动条的变化更新客户区的内容 。
■滚动条消息
当用户单击滚动条或拖动滑块时,Windows向窗口过程发送WM_VSCROLL消息或WM_HSCROLL消息,分别用于处理水平和垂直滚动条的操作。这些消息通常产生于用户直接互动,如拖动滚动条的滑块,点击滚动条的箭头等。
这两个消息的格式如下:
WM_HSCROLL
WM_VSCROLL
wParam = LOWORD(wParam) //(action)通知码
HIWORD(wParam) //滑块当前的位置
lParam = hwndScrollBar //跟踪条控件的句柄。如果为NULL,则消息由滚动条控件发送。
两个消息的参数中,wParam的低字节(LOWORD)表示具体的滚动操作(action),可以是以下的一些常数:
SB_LINEUP 或 SB_LINELEFT:向上或向左滚动一行。
SB_LINEDOWN 或 SB_LINERIGHT:向下或向右滚动一行。
SB_PAGEUP 或 SB_PAGELEFT:向上或向左滚动一页面。
SB_PAGEDOWN 或 SB_PAGERIGHT:向下或向右滚动一页面。
SB_THUMBPOSITION:滑块(thumb)已经拖动到新的位置,只有当用户释放滑块时才会发送。
SB_THUMBTRACK:滑块(thumb)正在被拖动。
当收到滚动条消息时,应用程序通常会在其消息处理函数中(WndProc函数),根据wParam的值进行相应的处理。这可能包括调用GetScrollPos获取当前滚动位置,调整内容的显示,然后使用SetScrollPos设置新的滚动位置。
3.2.2 第20练:简单的窗口滚动条
/*---------------------------------------------------------
SYSMETS.H -- 系统配置信息结构数组
-----------------------------------------------------------*/
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
//结构数组
struct
{
int iIndex; //索引
TCHAR * szLabel;//系统信息
TCHAR * szDesc;//描述
}
…(略)
/*------------------------------------------------------------------------
020 编程达人win32 API每日一练
第20个例子SYSMETS2.C:获取系统配置信息2---带滚动条的窗口
WM_VSCROLL消息
SetScrollRange函数
SetScrollPos函数
局限:1、超出滚动范围;
2、滑块大小和客户区不成比例
3、拖动滑块,没有实时滚动客户区
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
#include <tchar.h>
#include "sysmets.h" //定义系统信息结构数组及NUMLINES(显示行数)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("SysMets1");
…(略)
hwnd = CreateWindow(szAppName, TEXT("Get System Metrics No. 1"),
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;
static int cyClient; //新的客户区高度
static int iVscrollPos; //滚动条滑块位置,默认值0~100之间
HDC hdc;
int i,y;
PAINTSTRUCT ps;
TCHAR szBuffer[10];
TEXTMETRIC tm;//存储装置字体结构信息
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd); //获取设备环境句柄
//把程序当前的字体信息,存放到TEXTMETRIC结构变量中
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;//字符平均宽度
//tm.tmPitchAndFamily字体间距,tmPitchAndFamily字段的低位决定字体是否
//为等宽字体:1代表变宽字体,0代表等宽字体。
//字体间距cxCaps设为cxChar的1.5倍。1表示变宽字体
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
//字符行距=字符高+字符顶部空间的数目
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);//释放设备环境句柄
//设置所指定滚动条范围的最小值和最大值。
//SB_VERT垂直滚动调,FALSE不立即重绘滚动条,标准滚动条的默认范围是0到
//100,nMaxPos = -1,表示默认值
SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - 1, FALSE);
//设置所指定滚动条中的滚动按钮的位置。
SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);//TRUE重绘
return 0;
case WM_SIZE:
cyClient = HIWORD(lParam);//客户区的新高度
return 0;
//当窗口的标准垂直滚动条中有滚动的事件发生时,操作系统捕捉到后发送
//WM_VSCROLL到消息队列,然后GetMessage消息循环从消息队列中取出消息,并//DispatchMessage分发给操作系统调用WndProc。
//此实例暂时未处理键盘按键操作滚动条的消息
case WM_VSCROLL:
switch (LOWORD(wParam))//wParam低16位是滚动条值
{
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);//wParam高16位则指定滚动框的当前位
break;
default:
break;
}
//更新滑块位置,滑块位置在0~NUMLINES - 1之间
iVscrollPos = max(0, min(iVscrollPos, NUMLINES - 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);
…(略)
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
/******************************************************************************
WM_VSCROLL消息:当窗口的标准垂直滚动条中有滚动的事件发生时,在我们的窗口应用程序的消息队列中将产生一条WM_VSCROLL消息。
参数解释:
wParam
HIWORD指定在滚动框的当前位置
LOWORD是SB_THUMBPOSITION或SB_THUMBTRACK; 否则,将不使用该词。
LOWORD做为指定一个滚动条值,指出用户的滚动要求。这个参数可以是下列值之一。
SB_PAGEDOWN 向下滚动一页
SB_LINEDOWN 向下滚动一行
SB_PAGEUP 向上滚动一页
SB_LINEUP 向上滚动一行
SB_TOP 滚动到顶端
SB_BOTTOM 滚动到底部
SB_THUMBPOSITION 用户有拖动滚动框 (滑块),并释放鼠标按钮。HIWORD 指示在拖动操作结束时滚动框的位置。
SB_THUMBTRACK 用户正在拖动滚动框。直到用户释放鼠标按钮,反复发送此邮件。HIWORD 指示滚动框已被拖动到的位置。
lParam
如果lParam参数等于0,就说明它是窗口滚动条;如果等于滚动条窗口句柄,就说明它是滚动条控件。
返回值:如果应用程序处理此消息,则应返回零。
【备注】
SB_THUMBTRACK请求代码通常由在用户拖动滚动框时提供反馈的应用程序使用。
如果应用程序滚动窗口的内容,则它还必须使用SetScrollPos函数重置滚动框的位置。
请注意,WM_VSCROLL消息仅携带16位滚动框位置数据。因此,仅依靠WM_VSCROLL(和WM_HSCROLL)
获取滚动位置数据的应用程序实际的最大位置值为65,535。
但是,由于SetScrollInfo,SetScrollPos,SetScrollRange,GetScrollInfo,GetScrollPos
和GetScrollRange函数支持32位滚动条位置数据,因此有一种方法可以绕开WM_HSCROLL和WM_VSCROLL消息的16位障碍。
有关该技术的说明,请参见GetScrollInfo。
*******************************************************************************
SetScrollRange函数:设置所指定滚动条范围的最小值和最大值。
BOOL SetScrollRange(
HWND hWnd, //处理滚动条控件或具有标准滚动条的窗口,具体取决于nBar参数的值 。
int nBar, //指定要设置的滚动条。此参数可以是以下值之一。
//SB_CTL设置滚动条控件的范围。该 HWND参数必须是句柄滚动条控制。
//SB_HORZ设置窗口的标准水平滚动条的范围。
//SB_VERT设置窗口的标准垂直滚动条的范围。
int nMinPos,//指定最小滚动位置。
int nMaxPos,//指定最大滚动位置。
BOOL bRedraw//指定是否应重绘滚动条以反映更改。如果此参数为TRUE,则会重新绘制滚动条。如果为FALSE,则不会重画滚动条。
);
返回值
类型:布尔
如果函数成功,则返回值为非零。
如果函数失败,则返回值为零。要获取扩展的错误信息,请调用GetLastError。
【备注】
通过将nMinPos和 nMaxPos设置为相同的值,可以使用SetScrollRange隐藏滚动条 。
应用程序不应在处理滚动条消息时调用SetScrollRange函数来隐藏滚动条。新应用程序应使用ShowScrollBar函数隐藏滚动条。
如果调用SetScrollRange紧跟在调用SetScrollPos功能, bRedraw参数SetScrollPos必须为零,以防止滚动条被吸入两次。
标准滚动条的默认范围是0到100。滚动条控件的默认范围是空的( nMinPos和 nMaxPos参数值均为零)。由nMinPos和 nMaxPos参数指定的值之间的差不得大于MAXLONG的值。
因为指示滚动条位置的消息 WM_HSCROLL和 WM_VSCROLL限于位置数据的16位,所以对于SetScrollRange函数的 nMaxPos参数,仅依赖于这些消息来获取位置数据的应用程序的实际最大值为65,535 。但是,因为SetScrollInfo,SetScrollPos,SetScrollRange,GetScrollInfo,GetScrollPos和GetScrollRange函数支持32位滚动条位置数据,
所以有一种方法可以绕过WM_HSCROLL和 WM_VSCROLL消息的16位障碍 。有关该技术的说明,请参见GetScrollInfo。
如果nBar参数是SB_CTL并且hWnd参数指定的窗口不是系统滚动条控件,则系统将SBM_SETRANGE消息发送到该窗口以设置滚动条信息。
这使SetScrollRange可以在模仿滚动条的自定义控件上进行操作。如果窗口不处理SBM_SETRANGE消息,则SetScrollRange函数将失败。
*******************************************************************************
SetScrollPos函数:用于设置所指定滚动条中的滚动按钮的位置。
int SetScrollPos(
HWND hWnd,//处理滚动条控件或具有标准滚动条的窗口,具体取决于nBar参数的值。
int nBar,//指定要设置的滚动条。
int nPos,//指定滚动框的新位置。该位置必须在滚动范围内。
BOOL bRedraw//指定是否重新绘制滚动条以反映新的滚动框位置。如果此参数为TRUE,
则会重新绘制滚动条。如果为FALSE,则不会重画滚动条。
);
返回值
类型:int
如果函数成功,则返回值是滚动框的上一个位置。
如果函数失败,则返回值为零。要获取扩展的错误信息,请调用GetLastError。
*/
3-4 窗口滚动条
总结
1.上述实例中,窗口滚动条是作为窗口的一种风格样式添加的。在调用CreateWindow函数创建窗口时,将窗口的风格设置为WS_OVERLAPPEDWINDOW | WS_VSCROLL,选择添加垂直滚动条。
2.在处理WM_CREATE消息时,初始化窗口滚动条:调用SetScrollRange函数设置窗口滚动条的范围0~NUMLINS-1。并将滚动条滑块的当前位置设置为VscrollPos(初始值为0)。
3.处理垂直滚动条消息WM_VSCROLL时使用了一个switch结构。取WM_VSCROLL消息的wParam参数低字(通知码),分别将滚动条滑块的为向上或向下滚动一行,向上或向下滚动一页。如果是鼠标拖动滚动条滑块,则WM_VSCROLL消息的wParam参数的高字记录当前滑块的位置。接下来更新滑块的位置。【注意】先判断滑块位置是否超出范围。如果位置小于0,则等于0。如果位置大于NUMLINES – 1,则等于NUMLINES – 1。然后调用GetScrollPos函数获取滑块位置,如果和之前的位置不等,则调用SetScrollPos函数重新设置滑块位置,并调用InvalidateRect函数使窗口客户区无效,调用UpdateWindow函数立即重绘窗口。
4.虽然实例添加了窗口滚动条,可以显示所有内容,但是仍然具有3个局限:一是超出实际需要的滚动范围,存在空白区域。二是滑块大小和客户区大小不成比例。三是拖动滑块,没有实时滚动客户区。我们将在下一个实例中解决上述3个局限性。