最近开发一个Wtl自绘界面程序。我在WM_ERASEBKGND消息中绘制背景图,在客户区域的右上角绘制控制按钮。最初对话框是不可改变大小的,后来由于需求需要支持拖拉改变对话框大小,相应的控制按钮的位置也要随着对话框的大小变化而变化。于是我在WM_SIZE消息中改变控制按钮的位置:
void OnSize(UINT nType, CSize size)
{
::OutputDebugString(_T("OnSize\n"));
CWindow btnOK(GetDlgItem(IDOK));
RECT btnRC;
btnOK.GetClientRect(&btnRC);
btnOK.MoveWindow(size.cx - btnRC.right - 20, 10, btnRC.right, btnRC.bottom);
}
发现在拖拉改变对话框的大小时按钮的刷新不及时,经常发生按钮显示不完全或者完全消失的情况。后来调研得知是因为按钮处理WM_PAINT消息比较滞后的原因导致,于是我在OnSize函数中加入的对控制按钮的RedrawWindow Api的调用,控制按钮的显示才正常起来。
void OnSize(UINT nType, CSize size)
{
::OutputDebugString(_T("OnSize\n"));
CWindow btnOK(GetDlgItem(IDOK));
RECT btnRC;
btnOK.GetClientRect(&btnRC);
btnOK.MoveWindow(size.cx - btnRC.right - 20, 10, btnRC.right, btnRC.bottom);
btnOK.RedrawWindow();
}
RedrawWindow会直接调用窗口的WM_PAINT消息过程,并在消息处理完毕后返回。具体请Google其与Invalidate的区别。
我又测试了下WM_ERASEBKGND消息执行情况,发现在对话框程序中,WM_ERASEBKGND消息不断的发送到窗口,即使窗口不需要被重绘(原因未知,希望有牛人告知)。我怀疑这个消息的不断执行影响了界面的刷新,于是我更改了界面绘制流程,处理WM_ERASEBKGND消息直接返回TRUE,而在处理WM_PAINT消息中绘制背景图。发现即使不对按钮使用RedrawWindow函数,按钮也可以快速的刷新。
BOOL OnEraseBkgnd(HDC hDC)
{
return TRUE;
}
void OnPaint(CDCHandle dc)
{
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(&ps);
CRect rcClient;
GetClientRect(&rcClient);
//SetMsgHandled(FALSE);
Gdiplus::Bitmap bmp(rcClient.right,rcClient.bottom);
Gdiplus::Graphics graphics(&bmp);
graphics.DrawImage(m_pImage, Gdiplus::Rect(0,0,rcClient.Width(),rcClient.Height()),
0,0,rcClient.Width(),rcClient.Height(),Gdiplus::UnitPixel);
Gdiplus::Graphics g(hdc);
g.DrawImage(&bmp,0,0);
EndPaint(&ps);
::OutputDebugString(_T("---- OnPaint\n"));
}
void OnSize(UINT nType, CSize size)
{
::OutputDebugString(_T("OnSize\n"));
CWindow btnOK(GetDlgItem(IDOK));
RECT btnRC;
btnOK.GetClientRect(&btnRC);
btnOK.MoveWindow(size.cx - btnRC.right - 20, 10, btnRC.right, btnRC.bottom);
}
但是如果快速的改变对话框的大小,控制按钮还是少许的刷新不及时的现象,还是要加入对控制按钮的RedrawWindow函数的调用,这样就完美了。好了记住重绘对话框步骤:
1. 处理消息WM_ERASEBKGND返回TRUE
2. 在消息WM_PAINT绘制背景
3. 在对话框的WM_SIZE函数中处理子控件的重绘要调用子控件的RedrawWindow函数(据说在WM_WINDOWPOSCHANGED处理更高效)。
PS,以上为个人总结,非官方,非权威,若有异议,评论即可!