问题
有时候,我们碰到这样一个需求:需要对界面控件进行一系列操作,每一次操作都会导致界面重绘。希望能在操作开始之前进行不让界面重绘发生,直至全部操作结束才一次性重绘。
我们可以想到两个函数可能会帮我们做到这一点:LockWindowUpdate和SetRedraw。但是,应该用哪一个?
我们先看看MSDN对这两个函数的描述:
LockWindowUpdate
The LockWindowUpdate function disables or enables drawing in the specified window. Only one window can be locked at a time.
The purpose of the LockWindowUpdate function is to permit drag/drop feedback to be drawn over a window without interference from the window itself. The intent is that the window is locked when feedback is drawn and unlocked when feedback is complete. LockWindowUpdate is not intended for general-purpose suppression of window redraw. Use the WM_SETREDRAW message to disable redrawing of a particular window.
LockWindowUpdate使用场景之一:Drag/Drop操作
从以上描述可以知道,LockWindowUpdate主要用在Drag/Drop操作中,当我们拖拽某个窗口时,如果”拖动时显示窗口内容”这一选项被关闭,那么窗口管理器将会使用此函数锁定整个桌面,然后绘制点状的选择矩形。这个时候,因为整个桌面被锁定,所以拖拽时显示的点状矩形就不会受其他窗口的绘制所影响。当拖拽结束,桌面被解锁,所有窗口的绘制重新启用。需要注意的是,在同一时间,只有一个窗口处于这种锁定状态。
LockWindowUpdate使用场景之二:拖拽时显示自定义图标
另一个使用LockWindowUpdate的场景是在拖拽时显示自定义图标。在这种情况下,我们通过锁定指定的窗口,来绘制拖拽中显示的图标。在绘制图标的过程中,可能窗口的内容发生了变化,但是因为此时窗口被锁定了,所有不会发生窗口内容和我们绘制的内容相冲突的情况。
总结,LockWindowUpdate的使用场景中,都和窗口的拖拽有关。
比如,拖动窗口的标题来移动窗口,拖动窗口边框来调整窗口大小,拖动一个文件到窗口以及从窗口中拖动一个元素等。设计LockWindowUpdate这个API的目的就是为了更好的实现以上这些场景。上面提到过,在同一时间,只有一个窗口处于这种锁定状态,这是因为我们一般按住鼠标按钮实现拖动,这个时候,我们每次只能拖动一个窗口,所以每次我们只能锁定一个窗口。
SetRedraw
An application sends the WM_SETREDRAW message to a window to allow changes in that window to be redrawn or to prevent changes in that window from being redrawn.
This message can be useful if an application must add several items to a list box. The application can call this message with wParam set to FALSE, add the items, and then call the message again with wParam set to TRUE. Finally, the application can call RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN) to cause the list box to be repainted.
对于SetRedraw,其实现实际上是对指定窗口发送WM_SETREDRAW消息。此消息的作用是用来禁止窗口重绘。如MSDN描述中所说的,当我们向一个控件添加多个元素的时候,我们可以先调用此函数禁止窗口重绘,等全部元素添加完毕了,再启用窗口重绘,别忘了,启用重绘之后,我们需要调用RedrawWindow来触发窗口的重绘操作。示例代码如下:
如果这个时候,我们不使用SetRedraw,而是使用LockWindowUpdate,会怎么样?
之前我们提到过,LockWindowUpdate只能锁定全系统内的单个窗口。
如果当我们调用LockWindowUpdate的时候,碰巧,系统内的其他程序也调用LockWindowUpdate,则可能我们的调用会失效。这取决于函数是被谁先调用的。使用SetRedraw就不会有这个问题。
当我们使用LockWindowUpdate锁定了窗口,这个时候,用户切换到了另一个程序界面并尝试拖动这一窗口,想想会发生什么?此时LockWindowUpdate会失败,用户无法拖动窗口。
总结
当我们希望禁止窗口频繁重绘来避免闪烁时,使用SetRedraw,而不是LockWindowUpdate。