我有一个带有一个子窗口的大小对话框 - 一个列表控件。重新调整对话框大小后,我会适当地调整列表控件的大小;它基本上锚定在对话框的所有4个边缘。问题是在调整大小期间,列表控件的边缘周围会出现明显的闪烁,尤其是当存在滚动条时。我是Win32 GUI的新手,所以我真的不知道如何处理这个问题。我已经看过很多关于无闪烁绘图的文章,但它们都是关于单独的自定义绘制控件,并且它们都没有处理整个对话框的无闪烁绘制。如何在没有闪烁的情况下让它工作?
我的实际对话框显然有多个控件,但这是一个重现问题的最小代码示例(IDC_LIST1是Report视图中的列表控件,IDD_DIALOG2设置了WS_CLIPCHILDREN样式)。
#define NUM_COLUMNS 8
#define NUM_ROWS 32
RECT rcDialog2WindowOriginal;
RECT rcDialog2ClientOriginal;
RECT rcList1ClientOriginal;
INT_PTR Dialog2_OnInitDialog(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
GetWindowRect(hDlg, &rcDialog2WindowOriginal);
GetClientRect(hDlg, &rcDialog2ClientOriginal);
GetWindowRect(GetDlgItem(hDlg, IDC_LIST1), &rcList1ClientOriginal);
ScreenToClient(hDlg, ((LPPOINT)&rcList1ClientOriginal));
ScreenToClient(hDlg, ((LPPOINT)&rcList1ClientOriginal) + 1);
SendDlgItemMessage(hDlg, IDC_LIST1, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
TCHAR szText[32];
// add some columns
LVCOLUMN col;
ZeroMemory(&col, sizeof(LVCOLUMN));
col.mask = LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
col.cx = 60;
col.pszText = szText;
for(int i = 0; i < NUM_COLUMNS; i++)
{
col.iSubItem = i;
_stprintf_s(szText, 32, _T("Column %d"), col.iSubItem);
SendDlgItemMessage(hDlg, IDC_LIST1, LVM_INSERTCOLUMN, col.iSubItem, LPARAM)&col);
}
// add some items
LVITEM item;
ZeroMemory(&item, sizeof(LVITEM));
item.mask = LVIF_TEXT;
item.pszText = szText;
for(int i = 0; i < NUM_ROWS; i++)
{
item.iItem = i;
for(int j = 0; j < NUM_COLUMNS; j++)
{
item.iSubItem = j;
_stprintf_s(szText, 32, _T("Item %d, SubItem %d"), i, j);
if(j == 0)
{
SendDlgItemMessage(hDlg, IDC_LIST1, LVM_INSERTITEM, 0, (LPARAM)&item);
}
else
{
SendDlgItemMessage(hDlg, IDC_LIST1, LVM_SETITEM, 0, (LPARAM)&item);
}
}
}
// autosize the columns
for(int i = 0; i < NUM_COLUMNS; i++)
{
SendDlgItemMessage(hDlg, IDC_LIST1, LVM_SETCOLUMNWIDTH, i, LVSCW_AUTOSIZE);
}
return TRUE;
}
INT_PTR Dialog2_OnGetMinMaxInfo(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
LPMINMAXINFO lpMinMaxInfo = (LPMINMAXINFO)lParam;
// don't allow dialog to be resized smaller than original size
lpMinMaxInfo->ptMinTrackSize.x = rcDialog2WindowOriginal.right - rcDialog2WindowOriginal.left;
lpMinMaxInfo->ptMinTrackSize.y = rcDialog2WindowOriginal.bottom - rcDialog2WindowOriginal.top;
return TRUE;
}
INT_PTR Dialog2_OnSize(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
int cx = LOWORD(lParam);
int cy = HIWORD(lParam);
// anchor the list control to all edges of the dialog
int left_delta = rcList1ClientOriginal.left - rcDialog2ClientOriginal.left;
int right_delta = rcDialog2ClientOriginal.right - rcList1ClientOriginal.right;
int top_delta = rcList1ClientOriginal.top - rcDialog2ClientOriginal.top;
int bottom_delta = rcDialog2ClientOriginal.bottom - rcList1ClientOriginal.bottom;
int left = left_delta;
int top = top_delta;
int width = cx - left_delta - right_delta;
int height = cy - top_delta - bottom_delta;
HWND hWndList1 = GetDlgItem(hDlg, IDC_LIST1);
SetWindowPos(hWndList1, NULL, left, top, width, height, SWP_NOZORDER);
return TRUE;
}
INT_PTR Dialog2_OnClose(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
EndDialog(hDlg, IDOK);
return TRUE;
}
INT_PTR CALLBACK Dialog2_DialogProc(HWND hDlg, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
switch(nMsg)
{
case WM_INITDIALOG:
return Dialog2_OnInitDialog(hDlg, wParam, lParam);
case WM_GETMINMAXINFO:
return Dialog2_OnGetMinMaxInfo(hDlg, wParam, lParam);
case WM_SIZE:
return Dialog2_OnSize(hDlg, wParam, lParam);
case WM_CLOSE:
return Dialog2_OnClose(hDlg, wParam, lParam);
}
return FALSE;
}
更新
在查看了许多其他Windows应用程序(甚至是Microsoft编写的应用程序)之后,它们中的每一个都会遇到相同的闪烁问题。从左上角调整状态栏和滚动条的窗口大小时,尤其明显。我想我将不得不忍受它。
答案 0 :(得分:2)
很多闪烁来自WM_ERASEBKGRD,处理此消息并返回TRUE;
答案 1 :(得分:2)
你可以做几件事。
自己处理WM_ERASE。您可能仍需要擦除一些背景,但是您可以在窗口边缘绘制4个矩形以填充对话框的背景区域,而不会在子控件所在的中间过度绘制矩形。
调用SetWindowPos后,尝试调用UpdateWindow()立即重新绘制窗口(在列表视图控件和您自己的窗口中尝试)。这样可以最大限度地缩短尺寸更改和重绘完成之间的时间。
另一种方法是双缓冲(您将整个窗口表面绘制到屏幕外位图,并且只有在完成时才将其绘制到屏幕上。这样可以最大限度地缩短屏幕上更新过程的开始和结束之间的时间,因此减少了闪烁。虽然通过对话框和Windows控件很难实现,但在你的情况下并不适用。)
一般来说,不要害怕尝试事物。尝试以不同的顺序做事等,看看结果是什么。如果某些内容(如UpdateWindow())没有给出明显的改进,那么很容易再次删除代码,并尝试其他内容,直到获得最佳结果。
编辑 - 其他想法
更新控件时,您还可以暂停和恢复重新绘制(BeginUpdate()和EndUpdate()),以便在添加许多项目等时多次停止绘制。这对窗口调整大小不太有帮助虽然。
答案 2 :(得分:1)
在查看了许多其他Windows应用程序(甚至是Microsoft编写的应用程序)之后,它们中的每一个都会遇到相同的闪烁问题。从左上角调整状态栏和滚动条的窗口大小时,尤其明显。我想我将不得不忍受它。
答案 3 :(得分:0)
如果您尝试用以下代码替换任何窗口的默认 windowsproc,该窗口基本上会更新屏幕上的零像素(除了边框和标题栏;请参阅 WM_NCPAINT 和其他 NC 消息)。>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_ERASEBKGND:
// Lie and tell Windows that this window has cleared its background.
return (-1);
case WM_PAINT:
// Lie and tell Windows that this window has completed all its painting.
ValidateRect(hwnd, nullptr);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
这告诉我们您可以随意绘制像素。有或没有闪烁。这取决于你。
怎么样?
创建兼容位图(CreateCompatibleBitmap)并将其选择到 WM_PAINT 中窗口的设备上下文中。绘制完成后,BitBlt() 将该位图映射到实际窗口 DC,瞧;无闪烁渲染。