关于 ScrollWindow 的很多问题
本帖最后由 ShuRay 于 2013-03-08 19:57:27 编辑 大家好!我在看《Windows程序设计》第四章关于滚动条的实现时碰到了ScrollWindow这个函数,书中介绍的不是很详细,在网上搜到的资料说的也不是很明白,有几个疑问一直没弄懂,大家帮帮忙吧~先在此谢过啦!
先贴一下代码:
case WM_VSCROLL:
{
// 获取滚动条当前信息
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
// 保存当前位置
iVPos = si.nPos;
switch(LOWORD(wParam))
{
case SB_TOP:
si.nPos = si.nMin;
break;
case SB_BOTTOM:
si.nPos = si.nMax;
break;
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;
case SB_ENDSCROLL:
break;
case SB_THUMBPOSITION:
break;
default:
break;
}
//SetScrollInfo时如果pos不在range内,windows会自动调整,
//但要再次GetScrollInfo才能得到调整过后的正确位置pos
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
if (iVPos != si.nPos) //只有当滚动条位置改变时才执行以下操作
{
ScrollWindow(hwnd, 0, cyChar*(iVPos-si.nPos), NULL, NULL); //更新客户区
UpdateWindow(hwnd); //立即刷新,发送一个不进队的WM_PAINT消息
}
}
return 0;
case WM_PAINT:
{
// 获取滚动条信息
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
iVPos = si.nPos;
hdc = BeginPaint(hwnd, &ps);
iPaintBgn = max(si.nMin, iVPos + ps.rcPaint.top/cyChar);
iPaintEnd = min(si.nMax, iVPos + ps.rcPaint.bottom/cyChar);
for (int i = iPaintBgn; i <= iPaintEnd; i++)
{
int x, y;
y = cyChar*(i-iVPos);
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);
TCHAR szBuffer[10];
int iLength;
iLength = wsprintf(szBuffer, TEXT("%5d"), GetSystemMetrics(sysmetrics[i].iIndex));
TextOut(hdc, 22*cxCaps+40*cxChar, y, szBuffer, iLength);
SetTextAlign(hdc, TA_LEFT | TA_TOP);
}
EndPaint(hwnd, &ps);
}
return 0;
问题如下:
1. 我大约知道ScrollWindow(程序第50行)是滚动窗口的,但是它到底是怎样的一个实现原理呢?什么是“未被scrollWindow覆盖的区域”?
2. UpdateWindow(程序第51行)在这里似乎没有什么作用,网上查的说“只绘制未被滚动操作覆盖的一行,加快绘制速度”,没明白什么意思?
3. 在WM_PAINT下面,其实还是计算出了显示在客户区的行数的范围,只绘制了这个范围内的数据。把ScrollWindow(hwnd, 0, cyChar*(iVPos-si.nPos), NULL, NULL);替换成InvalidateRect(hwnd, NULL, TRUE);也能实现同样的效果,唯一不同的是使用InvalidateRect时屏幕会闪烁。感觉ScrollWindow在这里体现不出比InvalidateRect有更大的优越性? C++ Windows编程 滚动条 ScrollWindow
[解决办法]
ScrollWindow的操作是根据参数的滚动范围 算出 不会滚出显示区域的矩形,对这部分像素直接BitBlt到新的合适位置,对于滚动出现的新的矩形,擦除背景,设该矩形为无效区域
源代码中的紧随的UpdateWindow没有太大意义,窗口有无效区域会触发WM_PAINT,新的区域也会很及时的刷新
替换成InvalidateRect(hwnd, NULL, TRUE);效果是一样的,但由于刚擦除了整个背景紧接着又画图,两个显示的反差太大,在人看来就是一种闪烁
使用ScrollWindow,大部分的区域是一次性BitBlt更新的,没有擦除背景这个显示的中间状态,只有新出现的矩形经过 擦除 和 重绘的过程,所以闪烁现象不明显得多