本文共 2832 字,大约阅读时间需要 9 分钟。
在处理WM_PAINT消息期间,窗口过程在调用了BeginPaint()之后,整个客户区即变得有效。程序也可以调用ValidateRect()使客户区内的任意矩形区域变为有效。如是调用该函数使得整个客户区全部变为有效,则当前消息队列中的所有WM_PAINT消息都会被删除。
在处理WM_PAINT消息时必须成对调用BeginPaint()和EndPaint()。如果窗口过程不处理WM_PAINT消息,则必须将WM_PAINT消息传递给DefWindowProc()。该项函数依次调用BeginPaint()和EndPaint()。它仅仅使无效区域变得有效。
case WM_PAINT:
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
但是以下的方法是错误的。
case WM_PAINT:
return 0;
Windows发送WM_PAINT消息是因为客户区的一部分变得无效。如果不调用BeginPaint()或ValidateRect(),则无效区域永远也不会变得有效。这样以来Windows将不停地发送WM_PAINT消息。
程序调用BeginPaint时,Windows会适当填入PAINTSTRUCT结构的各个字段值。用户程序只使用前三个字段,其它字段由Windows内部使用。
如果该窗口的窗口类有背景刷,BeginPaint返回前用刷子擦除无效区域的背景。HelloWin程序中的窗口类中就有背景刷:
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
在大多数情况下,fErase被标志为FALSE(0),这意味着Windows已经擦除了无效矩形的背景。如果想在窗口处理程序中自己定义一些背景的擦除行为,可以自行处理WM_ERASEBKGN消息。当fErase被标记为TRUE时BeginPaint返回前向窗口发出WM_ERASEBKGN消息。
如果程序通过调用InvalidateRect使客户区中的矩形失效,则该函数的最后一个参数会指定是否擦除背景。如果这个参数为FALSE,则Windows将不会擦除背景,并且在调用完BeginPaint后PAINTSTRUCT结构的fErase字段将为TRUE(非零)。
保存si.Pos变化前的值。
if(si.nPos != iVertPos)
经过switch语句之后,如果si.nPos的值发生了改变,上面的if语句的条件就成了TRUE,然后执行里面的:
ScrollWindow(hwnd, 0, cyChar * (iVertPos - si.nPos), NULL, NULL);
UpdateWindow(hwnd);
ScrollWindow函数的功能是滚动客户区内容。假设滚动条接收到的是SB_LINEDOWN,si.nPos就会比iVertPos大,也就是说 iVertPos - si.nPos 的值是 -1,在这个时候ScrollWindow函数会把客户区的内容向上移动一行。当ScrollWindow函数的第三个参数为正数时,该函数会把客户区的内容下移。在一开始谈滚动条的时候就说过,当我们下拉滚动条的时候客户区的内容是向上移动的,一定要注意这一点。(Windows自动将显示区域中未被滚动操作覆盖的矩形设为无效,也就是将滚动所产生的空白区域设为无效。这会产生WM_PAINT消息。)
UpdateWindow函数通知Windows立即刷新客户区。(水平滚动条部分也是同样道理。)在呼叫InvalidateRect之后,Windows将WM_PAINT消息放入消息队列中,最后由窗口消息处理程序处理它。然而,Windows将WM_PAINT消息当成低优先级消息,如果系统有许多其它的动作正在发生,那么也许会让您等待一会儿工夫。如果希望立即更新无效区域,可以在呼叫InvalidateRect之后呼叫UpdateWindow。
WM_PAINTT部分
把垂直滚动条和水平滚动条的当前位置分别保存到iVertPos和iHorzPos中。
现在假设,一共有75行信息(0行到74行)需要显示,客户区能显示50行(0行到49行)。滚动条原来的位置是0。
用户把滚动条向下移动了两行,也就是说客户区的信息要向上移动两行,这个时候第0行、第1行已经看不见了,客户区顶部显示的是第2行的信息,而原来显示在客户区最后一行的第49行升到第47行了。这个时候第48行,第49行变成了空白区域。
ps.rcPaint.top 是该空白区域的左上角坐标。
ps.rcPaint.top / cyChar 就成了空白区域最上面一行的行数,跟据上面的假设,这是第48行。
客户区内容上移了两行,所以原来48行的位置上应该显示第50行的内容。然后在第49行显示的是第51行的内容。现在客户区显示了第2行到第51行的内容。
ps.rcPaint.bottom / cyChar 是空白区域最后一行的显示位置。
iPaintBeg = max (0, iVertPos + ps.rcPaint.top / cyChar);
iPaintEnd = min(NUMLINES - 1, iVertPos + ps.rcPaint.bottom / cyChar);
为什么要在 ps.rcPaint.top / cyChar 前面加上iVertPos呢?
虽然是第48行,但是要显示的内容是原来第50行的,也就是sysmetrics[50],所以要加上移动的行数iVertPos,这里iVertPos是2,所以正好能在第48行显示50行的内容。iPaintBeg与iPaintEnd也就是数组下标的开始与结束。
进入for循环。这个时候iPaintBeg的值是50,iPaintEnd的值是52。
y = cyChar * (i - iVertPos);
y的值变成48,因为第50行信息的显示位置是48,然后就是使用TextOut显示文本串了。
要显示的是第i行的内容(sysmetrics[i]),也就是第50行的内容,显示坐标y是第48行的坐标,所以程序将第50行的内容显示到第48行上,以此类推。
for循环的条件是i <= iPaintEnd,程序实际上多显示了一行,当滚动至最底部时,i值会大于数组边界,因此要限制一下变量的赋值范围。
x = cxChar * (1 - iHorzPos);
x坐标初始显示位置为cxChar * (1 - iHorzPos),与SYSMETS2不一样。
转载地址:http://risli.baihongyu.com/