Windows – How to eliminate flicker on a sizable dialog

cwindows

I've got a sizable dialog with one child window – a list control. When the dialog is re-sized, I re-size the list control appropriately; it is basically anchored to all 4 edges of the dialog. The problem is that during sizing there is noticeable flicker around the edges of the list control, especially when the scroll bars are present. I am a novice in Win32 GUI stuff, so I don't really know how to handle this. I've seen a lot of articles about flicker-free drawing, but they are all about individual custom-drawn controls and none of them deal with flicker-free drawing of a dialog as a whole. How can I get this to work without flickering so much?

My actual dialog has multiple controls obviously, but here is a minimal code example that reproduces the issue (IDC_LIST1 is a list control in Report view, IDD_DIALOG2 has WS_CLIPCHILDREN style set).

#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;
}

Update

After taking a look at many other Windows applications (even ones written by Microsoft), every single one of them suffers the same flickering issues. It is particularly evident when resizing a window with both a status bar and scroll bar from the top left. I guess I will just have to live with it.

Best Answer

A lot of flicker comes from WM_ERASEBKGRD, handle this message and just return TRUE;