如何在相当大的对话框上消除闪烁?

时间:2009-10-11 16:17:06

标签: c windows

我有一个带有一个子窗口的大小对话框 - 一个列表控件。重新调整对话框大小后,我会适当地调整列表控件的大小;它基本上锚定在对话框的所有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编写的应用程序)之后,它们中的每一个都会遇到相同的闪烁问题。从左上角调整状态栏和滚动条的窗口大小时,尤其明显。我想我将不得不忍受它。

4 个答案:

答案 0 :(得分:2)

很多闪烁来自WM_ERASEBKGRD,处理此消息并返回TRUE;

答案 1 :(得分:2)

你可以做几件事。

  • 自己处理WM_ERASE。您可能仍需要擦除一些背景,但是您可以在窗口边缘绘制4个矩形以填充对话框的背景区域,而不会在子控件所在的中间过度绘制矩形。

  • 调用SetWindowPos后,尝试调用UpdateWindow()立即重新绘制窗口(在列表视图控件和您自己的窗口中尝试)。这样可以最大限度地缩短尺寸更改和重绘完成之间的时间。

  • 另一种方法是双缓冲(您将整个窗口表面绘制到屏幕外位图,并且只有在完成时才将其绘制到屏幕上。这样可以最大限度地缩短屏幕上更新过程的开始和结束之间的时间,因此减少了闪烁。虽然通过对话框和Windows控件很难实现,但在你的情况下并不适用。)

  • 一般来说,不要害怕尝试事物。尝试以不同的顺序做事等,看看结果是什么。如果某些内容(如UpdateWindow())没有给出明显的改进,那么很容易再次删除代码,并尝试其他内容,直到获得最佳结果。

编辑 - 其他想法

  • 另见this SO question

  • 更新控件时,您还可以暂停和恢复重新绘制(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,瞧;无闪烁渲染。