2009-10-11 60 views
4

我有一个很大的对话框,有一个子窗口 - 一个列表控件。当对话框重新调整大小时,我会重新调整列表控件的大小;它基本上固定在对话框的所有4个边上。问题是,在调整大小时,列表控件的边缘周围会出现明显的闪烁,尤其是当滚动条出现时。我是Win32 GUI中的新手,所以我不知道如何处理这个问题。我见过很多关于无闪烁绘图的文章,但它们都是关于个人自定义绘制的控件,而且它们都不涉及整个对话框的无闪烁绘图。我怎样才能使这个工作没有闪烁这么多?如何消除大型对话框上的闪烁?

我的实际对话框显然有多个控件,但这里是一个重现问题的最小代码示例(IDC_LIST1是报表视图中的列表控件,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应用程序(甚至是那些由微软写的)采取一看后,他们中的每单一个遭受同样闪烁的问题。从左上角的状态栏和滚动条调整窗口大小时尤其明显。我想我只能忍受它。

+0

我真的没有任何闪烁(XP SP2,视觉风格开启和关闭)。哪个版本的Windows具有闪烁,并且Visual风格是否打开(exe文件中的清单)? – Anders 2009-10-11 21:56:03

+0

XP SP3。我包含comctl32清单,但是我关闭了视觉样式。尝试从顶部/左侧而不是右下角调整大小;那是闪烁最差的地方。 – Luke 2009-10-11 22:25:26

+0

是的,从顶部/左侧调整大小给出了一个“反弹”,它发生在大多数Windows应用程序,但我没有看到任何闪烁 – Anders 2009-10-12 14:55:01

回答

1

看过许多其他Windows应用程序(甚至是由Microsoft编写的应用程序)之后,它们中的每一个应用程序都会遇到相同的闪烁问题。从左上角的状态栏和滚动条调整窗口大小时尤其明显。我想我只能忍受它。

+0

你是对的,甚至记事本(Word Wrap关闭)仍然会产生闪烁。但是,如果打开Word Wrap,闪烁将消失。 – dns 2014-01-08 08:04:16

2

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

+0

子控件不覆盖整个对话框,所以如果你忽略WM_ERASEBKGRD那么对话框背景当它调整大小时从未被绘。 – Luke 2009-10-11 16:24:59

2

你可以做几件事。

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

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

  • 另一种方法是双缓冲(您将整个窗口表面绘制到离屏位图,并且只在完成时将其绘制到屏幕上,这样可以最小化屏幕上更新proicess的开始和结束之间的时间,所以减少了闪烁,这很难通过对话框和窗口控制来实现,所以不适用于你的情况)。

  • 一般来说,不要害怕尝试东西。尝试以不同的顺序进行操作等,看看结果如何。如果某些事情(如UpdateWindow())没有提供明显的改进,那么很容易再次移除代码,并尝试其他方法,直到获得最佳结果。

编辑 - 更多的想法

  • 也看到this SO question

  • 当更新控件,您还可以暂停和恢复重新粉刷(的BeginUpdate()和EndUpdate()),以阻止他们在添加多个项目时不止一次绘制等等。但这不可能帮助您调整窗口大小。

+0

处理WM_ERASEBKGRD没有帮助;闪烁似乎在列表控制的处理过程中发生。 UpdateWindow()没有效果;我认为SetWindowPos()已经发送了一个WM_PAINT消息。 我想看看双缓冲,但这似乎是一个控制的东西;似乎没有办法将双缓冲整个对话框。 Windows资源管理器与我的对话框具有相同的行为,所以如果微软无法做到这一点,我想我只能忍受闪烁。 – Luke 2009-10-11 20:15:09

+0

尝试WM_ERASE - WM_ERASEBKGRD可能会有不同的行为(我在Win32中编写了16年的用户界面,并且从未听说过或使用过WM_ERASEBKGRD)。另请参阅编辑我的答案。 – 2009-10-11 20:36:26