前言:
在上一篇滚动条的使用中,存在一些问题,比如:拉着滚动条快速移动会出现闪烁,还有就是效率不高,有卡顿等。为优化这一功能,这里将介绍一种更好的方法。
效果图:
一、准备知识
1. 结构SCROLLINFO
结构原型:
typedef struct tagSCROLLINFO
{
UINT cbSize; // SCROLLINFO结构体本身的字节大小
UINT fMask; // 见下面的说明
int nMin; // 最小滚动位置
int nMax; // 最大滚动位置
UINT nPage; // 页面尺寸
int nPos; // 滚动块的位置
int nTrackPos; // 滚动块当前被拖动的位置,不能在SetScrollInfo中指定
} SCROLLINFO;
参数解释:
cbSize: SCROLLINFO结构长度字节数,该值在设置和查询参数时都必须填写。
fMask: 指定结构中的哪些成员是有效,该值共有如下5种选择,可以选择多种用“OR”组合起来,该值在设置和查询参数时都必须填写。
值 | 含义 |
---|---|
SIF_ALL | 整个结构都有效 |
SIF_DISABLENOSCROLL | 该值仅在设定参数时使用,视控件参数设定的需要来对本结构的成员进行取舍。 |
SIF_PAGE | nPage成员有效 |
SIF_POS | nPos成员有效 |
SIF_RANGE | nMin和nMax成员有效 |
nMin: 滚动范围最小值
nMax: 滚动范围最大值
nPage: 页尺寸,用来确定比例滚动框的大小
nPos: 滚动框的位置
nTrackPos: 拖动时滚动框的位置,该参数只能查询,不能设置。
2. 函数SetScrollInfo
函数功能:
该函数设置滚动条参数,包括滚动位置的最大值和最小值,页面大小,滚动按钮的位置。如被请求,函数也可以重画滚动条。
函数原型:
int SetScrollInfo(HWND hWnd;int fnBar,LPSCROLLINFO lpsi,BOOL fRedraw);
参数解释:
hWnd:滚动条控制或带标准滚动条的窗体句柄,由fnBar参数决定。
fnBar:指定被设定参数的滚动条的类型。这个参数可以是下面值,含义如下:
SB_CTL:设置滚动条控制。而参数hwnd必须是滚动条控制的句柄。
SB_HORZ:设置所给定的窗体上标准水平滚动条参数。
SB_VERT:设置所给定的窗体上标准垂直滚动条参数。
IPBI:指向SCROLLINFO结构。在调用SetScrognfo之前,设置SCROLLINFO结构中cbSize成员以标识结构大小,设置成员fMask以说明待设置的滚动条参数,并且在适当的成员中制定新的参数值。
fRedraw:指定滚动条是否重画以反映滚动条的变化。如果这个参数为TRUE,滚动条将被重画,否则不被重画。
3. 函数GetScrollInfo
函数功能:
该函数找到滚动条的参数,包括滚动条位置的最小值、最大值,页面大小,滚动按钮的位置等。
函数原型:
BOOL GetScrollInfo( HWND hWnd, int fnBar, LPSCROLLINFO lpsi );
参数解释:
hWnd: 滚动条控制或有标准滚动条的窗体句柄,由fnBar参数确定。
fnBar: 指定待找回滚动条参数的类型,此参数可以为如下值,其值含义:
SB_CTL|找回滚动条控制参数。其中参数hwnd一定是处理滚动条控制的句柄。
SB_HORZ|找回所指定窗体的标准水平滚动条参数。
SB_VERT|找回所指定窗体的标准垂直滚动条参数。
lpsi:指向SCROLLINFO结构。
4. 函数ScrollWindow
函数功能:
该函数滚动所指定的窗口客户区域内容。该函数存在向后兼容性,新的应用程序应使用ScrollWindowEX。
函数原型:
BOOL ScrollWindow(HWND hWnd, int XAmount, int YAmount, CONST RECT *IpRect, CONST RECT *lpClipRect);
参数解释:
hWnd 客户区域将被滚动的窗口的句柄。
XAmount
指定水平滚动的距离,以设备单位计。如果窗口类风格为CS_OWNDC或CS_CLASSDC,则此参数则使用逻辑单位而非设备单位。当向左滚动窗体内容时,参数值必须为负。
YAmount
指定垂直滚动的距离,以设备单位计。如果窗口类风格为CS_OWNDC或CS_CLASSDC,则此参数则使用逻辑单位而非设备单位。当向上滚动窗体内容时,参数值必须为负。
lpRect
指向RECT结构的指针,该结构指定了将要滚动的客户区范围。若此参数为NULL,则整个客户区域将被滚动。
lpClipRect
指向RECT结构的指针,该结构指定了要滚动的裁剪区域。只有这个矩形中的位才会被滚动。在矩形之外的位不会被影响,即使它们是在lpRect矩形之内。(见代码"测试一")假如lpClipRect为NULL,则不会在滚动矩形上进行裁剪。
5. 核心要点
其实SetScrollInfo是SetScrollRange和SetScrollPos的结合,GetScrollInfo是GetScrollRange和GetScrollPos的结合。
无论是Set还是Get,都得先设置si结构的第一个域的值,即赋给cbSize结构的大小。之后根据设置的fMask域的值进行Set或Get,当Set时,需要根据fMask的值将相关的域填充后再调用SetScrollInfo(),这样si结构就被Set成功。当Get时,直接调用GetScrollInfo(),具体能使用哪些域的值是根据所设置的fMask域的值定的。
当要设置滚动条的范围和页面大小时(SetScrollInfo的使用):
si.cbSize = sizeof (SCROLLINFO) ;
si.fMask = SIF_RANGE | SIF_PAGE ;
si.nMin = 0 ;
si.nMax = NUMLINES - 1 ;
si.nPage = cyClient / cyChar ;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
而若要用到滚动条的位置时(GetScrollInfo的使用):
先si.cbSize = sizeof (si) ;
si.fMask = SIF_ALL ; // 表示Get后将使用si结构的位置、页面大小等量
GetScrollInfo (hwnd, SB_VERT, &si) ;
// 然后就可直接使用si.nPos、si.nPage、si.nTrackPos等量,
// 这些量就是从si结构中通过Get函数获得的,也是之前通过Set函数设置的值。
二、代码如下
这里提供的是相对Win32应用程序基本框架有修改添加的部分,其他部分一样,你只需此代码覆盖原来这部分代码即可。
CreateWindowW函数
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW | WS_VSCROLL,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
WndProc函数
RESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int i;
size_t j;
TCHAR str[100];
TEXTMETRIC tm;
SCROLLINFO si;
static int cxChar, cyChar, iVscrollPos, cxClient, cyClient, y;
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_CREATE: // 获得字体的大小、高度、宽度等信息
{
HDC hdc;
hdc = GetDC(hWnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hWnd, hdc);
}
case WM_SIZE:
{
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
y = cyClient / cyChar; // y表示一页里有多少行
si.cbSize = sizeof(SCROLLINFO); // 以下为设置滚动条
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = 50;
si.nPage = y;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
si.cbSize = sizeof(si); // 以下获得滚动条信息
si.fMask = SIF_POS;
GetScrollInfo(hWnd, SB_VERT, &si);
iVscrollPos = si.nPos;
for (i = 0; i < 50; i++)
{
SetTextAlign(hdc, TA_LEFT | TA_TOP);
StringCchPrintf(str, 100, TEXT("%d:I love you!"), i + 1);
StringCchLength(str, 100, &j);
TextOut(hdc, 0, (i - iVscrollPos)* cyChar, str, j);
TextOut(hdc, 20 * cxChar, (i - iVscrollPos)*cyChar, str, j);
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, 50 * cxChar, (i - iVscrollPos)*cyChar, str, j);
}
EndPaint(hWnd, &ps);
}
break;
case WM_VSCROLL:
{
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hWnd, SB_VERT, &si);
iVscrollPos = si.nPos;
switch (LOWORD(wParam))
{
case SB_LINEDOWN:
si.nPos += 1;
break;
case SB_LINEUP:
si.nPos -= 1;
break;
case SB_PAGEDOWN:
si.nPos += y;
break;
case SB_PAGEUP:
si.nPos -= y;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos ;
break;
}
si.nPos = min(50 - y, max(0, si.nPos));
si.fMask = SIF_POS;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
GetScrollInfo(hWnd, SB_VERT, &si);
if (iVscrollPos != si.nPos)
{
ScrollWindow(hWnd, 0, cyChar * (iVscrollPos - si.nPos), nullptr, nullptr);
UpdateWindow(hWnd);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
今日说:
今天刷一下空间,咋都是秀恩爱的啊,原来今天是七夕,但也跟我没多大关系,哈哈哈