滚动条的使用
1、 滚动条的创建
在创建窗口的CreateWindow函数的风格参数中加上WM_VSCROLL 或 WM_HSCROLL风格,这样创建出来的窗口就带了滚动条。这里,滚动条不是单独的窗口,它只是窗口的一部分(可以用spy++验证),当加入滚动条后,滚动条嵌入的区域不算在客户区内,客户区自动缩进了。创建语句如下:
HWND hWnd;
hWnd = CreateWindow(pszClassName,
_T("MyApplication"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL, //添加垂直滚动条
CW_USEDEFAULT,
CW_USEDEFAULT,
600,
480,
NULL,
NULL,
hInstance,
NULL);
2、滚动条消息的产生
当用户通过鼠标操作滚动条时,就会产生WM_VSCROLL消息(或者WM_HSCROLL消息),在程序中根据此消息的通知码(wParam参数)来做出相应的反应。这一过程是通过程序和Windows共同负责的,维护滚动条以及滑块在滚动条中的位置。
对于Windows而言:
它主要向拥有滚动条的窗口发送滚动条消息;
对于程序而言:
首先初始化滚动条的位置和滚动范围;
而后根据通知码来更新滑块的位置,并且更新客户区域的内容。
3、滚动条的消息的通知码:
产生WM_VSCROLL消息或WM_HSCROLL消息时,wParam参数的低位字代表了通知码,即鼠标在滚动条上的操作,高位字为滚动条的位置(SB_THUMBPOSITION、SB_THUMBTRACK通知码时才有用)。lParam这里为NULL,如果这个消息是通过滚动条控件发生的,那么该参数为控件的句柄。对通知码的说明见下图:
#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 //同上
#defineSB_ENDSCROLL 8
说明:
1、 对于达到最值的通知码(SB_TOP、SB_LEFT、SB_BOTTOM、SB_RIGHT),当滚动条是窗口的一部分时,则不会收到这些通知码。
2、 对于橙色的通知码,当鼠标释放时,会产生带有SB_ENDSCROLL通知码的滚动条消息,而其他两个通知码是对应的(鼠标释放和鼠标按下)
4、示例代码
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NON_CONFORMING_SWPRINTFS
#define MAX_NUM_LINE 100 //显示文字的最大行数
#include <windows.h>
#include <tchar.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE hPreInstance,
LPTSTR lpCmdLine,
int nShowCmd)
{
TCHAR *pszClassName = _T("MyClass");
WNDCLASS wndClass;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpszClassName = pszClassName;
wndClass.lpszMenuName = NULL;
BOOL bRet;
bRet = RegisterClass(&wndClass);
if (!bRet)
{
MessageBox(NULL, _T("注册窗口类失败"), NULL, MB_OK);
return FALSE;
}
HWND hWnd;
hWnd = CreateWindow(pszClassName,
_T("MyApplication"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL, //添加垂直滚动条
CW_USEDEFAULT,
CW_USEDEFAULT,
600,
480,
NULL,
NULL,
hInstance,
NULL);
if (!hWnd)
{
MessageBox(NULL, _T("创建窗口失败!"), NULL, MB_OK);
return FALSE;
}
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//窗口过程
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int cyChar; //一个字符的高度
static int iVscrollPos = 0; //垂直滚动条所在位置
static int cyClient; //客户区的高度
TEXTMETRIC tm;
PAINTSTRUCT ps;
HDC hdc;
switch (uMsg)
{
case WM_CREATE:
hdc = GetDC(hWnd);
GetTextMetrics(hdc, &tm);
cyChar = tm.tmHeight + tm.tmExternalLeading; //每个字符的高和每行文字应该预留的间隔(可能为0)
ReleaseDC(hWnd, hdc);
SetScrollRange(hWnd, SB_VERT, 0, MAX_NUM_LINE, FALSE); //设置滚动条滚动范围
SetScrollPos(hWnd, SB_VERT, iVscrollPos, TRUE); //设置滚动条滑块位置
return 0;
case WM_SIZE:
cyClient = HIWORD(lParam);
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for (int i = 0; i < MAX_NUM_LINE; ++i) //显示 MAX_NUM_LINE 行文字
{
TCHAR buffer[100];
_stprintf(buffer, _T("第%4d 行"), i + 1);
//TextOut(hdc, 0, i * cyChar, buffer, _tcslen(buffer));
TextOut(hdc, //更新客户区显示的内容
0,
(i - iVscrollPos) * cyChar, //实质是显示的位置发生了改变
buffer,
_tcslen(buffer));
}
EndPaint(hWnd, &ps);
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_THUMBTRACK: //如果鼠标不松开,此消息将不断产生
iVscrollPos = HIWORD(wParam);
break;
case SB_THUMBPOSITION: //鼠标松开时,产生此消息
iVscrollPos = HIWORD(wParam);
break;
}
iVscrollPos = max(0, min(iVscrollPos, MAX_NUM_LINE)); //确保滑块位置在滚动的范围内
if (iVscrollPos != GetScrollPos(hWnd, SB_VERT))
{
SetScrollPos(hWnd, SB_VERT, iVscrollPos, TRUE); //设置滑块位置
InvalidateRect(hWnd, NULL, TRUE); //更新客户区的内容
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
5、运行结果如下
缺点:当显示的行数太大时,则实时性有一定的缓慢;最后一行信息不应该显示到客户区的顶部,即某些文本显示要有适当的位置。
6、相关函数说明
BOOL SetScrollRange(
HWND hWnd, // handle to window
intnBar, // scroll bar
intnMinPos, // minimum scrolling position
intnMaxPos, // maximum scrolling position
BOOL bRedraw // redraw flag
);
设置滚动条的滚动范围,参数说明如下:
hWnd:滚动条所属的窗口句柄,如果是控件,那么则是控件的句柄
nBar:滚动条的类型,可以是值的其中一个:
SB_CTL | 该滚动条是一个滚动条控件 |
SB_HORZ | 窗口的标准水平滚动条 |
SB_VERT | 窗口的标准垂直滚动条 |
nMinPos:滚动范围的最小值
nMaxPos:滚动范围的最大值
bRedraw :设置范围后,决定是否根据新的范围来重绘滚动条
BOOL GetScrollRange(
HWND hWnd, // handle to window
int nBar, // scroll bar options
LPINTlpMinPos, // receives minimum position
LPINTlpMaxPos // receives maximum position);
获取滚动条的滚动范围
int SetScrollPos(
HWND hWnd, // handle to window
int nBar, //scroll bar
int nPos, //new position of scroll box
BOOLbRedraw // redraw flag);
设置滚动条滑块的位置,该值是一个32位的数据,而wParam的高位字却是16位,在处理时应该注意到这点
int SetScrollInfo(
HWND hwnd, // handle to window
int fnBar, //scroll bar type
LPCSCROLLINFO lpsi, //scroll parameters
BOOLfRedraw // redraw flag
);
设置滚动条的相关信息,包括滚动范围、滑块位置、页大小,也决定是否重绘
主要说明的是SCROLLINFO结构体:
typedef struct tagSCROLLINFO {
UINT cbSize; //结构体大小
UINT fMask; //掩码,指定要操作哪些属性
int nMin; //最小范围
int nMax; //最大范围
UINT nPage; //页大小,即滑块的大小,这个值决定它所占滚动条的大小,其值在滚动范围之间,如果超过了范围,那么滚动条将消失,动态改变它的大小可以表达文档显示的完整度
int nPos; //滑块位置
int nTrackPos; //滑块在滚动中的位置,在处理SB_THUMBTRACK时可获取
} SCROLLINFO, *LPSCROLLINFO;
BOOL GetScrollInfo(
HWND hwnd, // handle to window
int fnBar, // scroll bar type
LPSCROLLINFO lpsi // scroll bar parameters);
获取滚动条的相关信息
7、滚动条相关消息
SBM_ENABLE_ARROWS //操作箭头按钮
SBM_GETPOS
SBM_GETRANGE
SBM_GETSCROLLBARINFO
SBM_GETSCROLLINFO
SBM_SETPOS
SBM_SETRANGE
SBM_SETRANGEREDRAW
SBM_SETSCROLLINFO