前面简单介绍了滚动条的使用,以及滚动条相关的函数。下面将基于一下内容继续完善滚动条的使用:
1:GetScrollInfo、SetScrollInfo函数的使用
2:解决绘制效率问题、以及最后最后一条数据不能在客户区底部显示的问题
1、GetScrollInfo、SetScrollInfo函数的使用
这两个函数可以完全取代前面所使用的那一系列函数:GetScrollRange、GetScrollPos、SetScrollRange、SetScrollPos。因为GetScrollInfo、SetScrollInfo所使用的参数为SCROLLINFO结构体,该结构体包含了滚动条相关的信息,如下:
typedef struct tagSCROLLINFO {
UINT cbSize;
UINT fMask;
int nMin;
int nMax;
UINT nPage;
int nPos;
int nTrackPos;
} SCROLLINFO, *LPSCROLLINFO;
typedef SCROLLINFO CONST *LPCSCROLLINFO;
现在对这些参数重新说明:
cbSize:结构体大小
fMask:掩码,指定要操作(设置或则获取)哪些滚动条的参数
SIF_ALL 包含 SIF_PAGE, SIF_POS, SIF_RANGE, SIF_TRACKPOS
SIF_DISABLENOSCROLL 时滚动条无效
SIF_PAGE 操作也参数
SIF_POS 操作位置
SIF_RANGE 操作范围
SIF_TRACKPOS 获取拖动的位置
nMin:滚动条滚动范围的最小值
nMax:滚动条滚动范围的最大值
nPage:页面大小,即一页所占的内容大小(范围),这个值可以动态变化来改变滑块的大小
nPos:滚动条的位置
nTrackPos:滚动条在拖动过程中的位置
注意:
正因为有了这个结构体,我们操作滚动条才变得更加方便,MSDN中建议我们在新的应用程序中使用GetScrollInfo、SetScrollInfo来操作滚动条。
另外,系统会根据滚动条的范围和页(page)大小来计算滚动的真正范围,这就解决了最后一条数据不能现在客户区底部的问题
2、源代码如下
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NON_CONFORMING_SWPRINTFS
#define MAX_NUM_ROW 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 | WS_HSCROLL, //添加垂直滚动条和水平滚动条
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 cxClient, cyClient, cxChar, cyChar, cxCaps;
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
SCROLLINFO scrollInfo;
int iVertPos, iHorzPos;
int iBegin, iEnd;
switch (uMsg)
{
case WM_CREATE:
hdc = GetDC(hWnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;
cxCaps = (tm.tmPitchAndFamily & TMPF_FIXED_PITCH ? 3 : 2) * cxChar / 2;
ReleaseDC(hWnd, hdc);
return 0;
case WM_SIZE:
//第一条WM_SIZE消息是在WinMain函数调用CraeteWindow后,在ShowWindow时产生的,
//所以这里获取客户区的大小进行初始化是可以的
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_PAGE | SIF_RANGE;
//设置垂直滚动条
scrollInfo.nMin = 0;
scrollInfo.nMax = MAX_NUM_ROW -1;
scrollInfo.nPage = cyClient / cyChar; //更新
SetScrollInfo(hWnd, SB_VERT, &scrollInfo, TRUE);
//设置水平滚动条
scrollInfo.nMin = 0;
scrollInfo.nMax = 100;
scrollInfo.nPage = cxClient / cxChar;
SetScrollInfo(hWnd, SB_HORZ, &scrollInfo, TRUE);
return 0;
case WM_VSCROLL:
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
iVertPos = scrollInfo.nPos;
switch (LOWORD(wParam))
{
case SB_LINEUP:
scrollInfo.nPos -= 1;
break;
case SB_LINEDOWN:
scrollInfo.nPos += 1;
break;
case SB_PAGEUP:
scrollInfo.nPos -= scrollInfo.nPage;
break;
case SB_PAGEDOWN:
scrollInfo.nPos += scrollInfo.nPage;
break;
case SB_THUMBTRACK:
scrollInfo.nPos = scrollInfo.nTrackPos;
break;
}
scrollInfo.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_VERT, &scrollInfo, TRUE);
GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
if (iVertPos != scrollInfo.nPos)
{
//滚动客户区的内容,这里参数设置代表了整个客户区,滚动后,没有覆盖的区域将参与重绘
ScrollWindow(hWnd,
0,
(iVertPos - scrollInfo.nPos) * cyChar, //这里为内容的滚动方向,向上则加上负值
NULL,
NULL);
UpdateWindow(hWnd); //立即更新无效的区域,此处为未被覆盖的局域
}
return 0;
case WM_HSCROLL:
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_HORZ, &scrollInfo);
iHorzPos = scrollInfo.nPos;
switch (LOWORD(wParam))
{
case SB_LINELEFT:
scrollInfo.nPos -= 1;
break;
case SB_LINERIGHT:
scrollInfo.nPos += 1;
break;
case SB_PAGELEFT:
scrollInfo.nPos -= scrollInfo.nPage;
break;
case SB_PAGERIGHT:
scrollInfo.nPos += scrollInfo.nPage;
break;
case SB_THUMBTRACK:
scrollInfo.nPos = scrollInfo.nTrackPos;
break;
}
scrollInfo.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_HORZ, &scrollInfo, TRUE);
GetScrollInfo(hWnd, SB_HORZ, &scrollInfo);
if (scrollInfo.nPos != iHorzPos)
{
ScrollWindow(hWnd, cxChar * (iHorzPos - scrollInfo.nPos),0, NULL, NULL);
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_POS;
GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
iVertPos = scrollInfo.nPos; //当前垂直滚动条的位置
GetScrollInfo(hWnd, SB_HORZ, &scrollInfo);
iHorzPos = scrollInfo.nPos; //当前水平滚动条的位置
//确保滚动条滑块位置在 0--MAX_NUM_ROW之间
iBegin = max(0, iVertPos + ps.rcPaint.top / cyChar);//无效区域对应的开始行,这句话是多余的,
//因为开始设置的iVertPos大于0,而无效区域的矩形的坐标一般也是个正直
//(InValidate可以设置坐标为负的矩形,但windows会做处理。对于不同的映射模式也一样吗?)
iEnd = min(MAX_NUM_ROW - 1, iVertPos + ps.rcPaint.bottom / cyChar); //无效区域对应的结束行
for (int row = iBegin; row <= iEnd; ++row)
{
TCHAR buffer[100] = {0};
TextOut(hdc,
-iHorzPos * cxChar,
(row - iVertPos) * cyChar,
buffer,
_stprintf(buffer, _T("第%3d行"), row + 1));
}
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
3、运行结果如下图
显然,解决了最后一行不能显示在客户区底部的问题。此外这里绘制是根据客户区无效的区域来确定对应内容的显示。当行号设置得更大时,绘制的效率也是一样的。
4、ScrollWindow的使用
BOOL ScrollWindow( HWND hWnd, // handle to window
int XAmount, // horizontal scrolling
int YAmount, // vertical scrolling
CONST RECT *lpRect, // client area
CONST RECT *lpClipRect // clipping rectangle);
见下图:
左边为滚动前的内容,右边为滚动后的内容。
其中红色区域为滚动区域,将内容向上滚动一行后,右边蓝色矩形区域为未被滚动区域覆盖的,那么这部分会被重绘。而红色区域不参与重绘,只是滚动之后的结果,内容不变,只是位置发生了改变而已!