WM_SETREDRAW блокирует отображение нарисованного владельцем заголовка списка

Я следовал совету по использованию WM_SETREDRAW вместо LockWindowUpdate () для подавления обновления списка при заполнении элементов. Я обнаружил, что моя строка заголовка не отображается после отправки сообщения WM_SETREDRAW с true. Комбинации InvalidateRect() и UpdateWindow() или RedrawWindow() не могут отобразить заголовок, нарисованный владельцем. Чтобы показать строку заголовка, мне нужно вручную форсировать новое сообщение WM_PAINT путем изменения размера или прокрутки экрана просмотра списка. Такое поведение ограничено владельцем нарисованного заголовка. Если я не подклассифицирую процедуру заголовка, процедура родного окна просто отображает строку заголовка после того, как сообщение WM_SETREDRAW отправлено с true. Кто-нибудь может подсказать, чего не хватает в моем коде?

Заметки:

  1. Использование WM_SETREDRAW оказалось гораздо более эффективным, чем LockWindowUpdae(). Для большого списка заполнение элементов и их отображение заняли 1,9 с, а не 9,6 с при использовании LockWindowUpdate(). Я надеюсь, что смогу правильно отобразить заголовок с помощью WM_SETREDRAW, чтобы повысить производительность.
  2. При создании списка без добавления элементов (и без отправки сообщения WM_SETREDRAW) собственная процедура Windows отображает заголовок, но подклассовая процедура не отображает его, пока я вручную не переместлю или не изменим размер окна. Снова InvalidRect()/UpdateWindow() не имеют никакого эффекта.

Вот код для подклассов:

static LONG_PTR   DefaultHeaderProc ;
static char* HeaderText[] = {"A","B","C","D","E","F","G","H","I","J",
                             "K","L","M","N","O","P","Q","R","S","T"} ;

LRESULT CALLBACK ListDlgProc (HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam)
{
static HWND     hwndHeader ;
static HWND     hwndListView ;


switch (msg)  {

    case WM_INITDIALOG :

        hwndListView = GetDlgItem (hDlg,IDC_LISTVIEW) ;
        hwndHeader   = ListView_GetHeader (hwndListView) ;

        ListView_SetExtendedListViewStyle (hwndListView,LVS_EX_GRIDLINES | 
                    LVS_EX_TRANSPARENTBKGND |LVS_EX_FULLROWSELECT) ;

        SendMessage (hwndListView,LVM_SETBKCOLOR,0,(LPARAM) 0xE0E0E0) ;
        SendMessage (hwndListView,LVM_SETTEXTCOLOR,0,(LPARAM) 0xC00000) ;

        // Initialize the LVCOLUMN structure. 
        LVCOLUMN    LvColumn ;
        ZeroMemory (&LvColumn,sizeof (LVCOLUMN)) ;
        LvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT ; 
        LvColumn.fmt = LVCFMT_LEFT ;
        LvColumn.cx = 63  ;

        // Add the columns 
        for (int i = 0 ; i < COLUMNS ; i++) { 
            LvColumn.pszText = HeaderText[i] ;
            LvColumn.iSubItem = i ; 
            if (ListView_InsertColumn(hwndListView,i,&LvColumn) == -1) 
                return false ; 
        } /* for (int i = 0 ; i < COLUMNS ; i++) */

        DefaultHeaderProc = SetWindowLongPtr (hwndHeader,GWLP_WNDPROC,
                                 (LONG_PTR) HeaderSubclassProc) ;
        SendMessage (hwndHeader,IDM_INIT,0,0L) ;
        return true ;

    case WM_DESTROY :
        SetWindowLongPtr (hwndHeader,GWLP_WNDPROC,(LONG_PTR) DefaultHeaderProc) ;
        SetWindowLongPtr (hwndListView,GWLP_WNDPROC,(LONG_PTR) DefaultListViewProc) ;
        ListView_DeleteAllItems (hwndListView) ;
        return 0 ;
       ;

Вот код для обработки WM_PAINT:

    case WM_PAINT :
        PAINTSTRUCT ps ;
        hDC = BeginPaint (hwnd,&ps) ;

        int DefDC = SaveDC (hDC) ;

        SelectObject (hDC,Font) ;
        SelectObject (hDC,Pen) ;
        SelectObject (hDC,Brush) ;
        SetBkMode (hDC,TRANSPARENT) ;

        for (int Btn = 0 ; Btn < 20 ; Btn++) {
            if (Btn == HBtn)
                continue ;
            Header_GetItemRect (hwnd,Btn,&rc) ;
            Rectangle (hDC,rc.left,rc.top,rc.right + 1,rc.bottom) ;
            DrawText (hDC,HeaderText[Btn],-1,&rc,DT_CENTER | DT_VCENTER) ;
        } /* for (int Btn = 0 ; Btn < 20 ; Btn++) */

        if (HBtn > -1 ) {
            Header_GetItemRect (hwnd,HBtn,&rc) ;
            SelectObject (hDC,HPen) ;
            SelectObject (hDC,HBrush) ;
            Rectangle (hDC,rc.left + 1,rc.top + 1,rc.right,rc.bottom - 1) ;
            DrawText (hDC,HeaderText[HBtn],-1,&rc,DT_CENTER | DT_VCENTER) ;
        } /* if (HLBtn > -1 ) */

        RestoreDC (hDC,DefDC) ;
        EndPaint (hwnd,&ps) ;
        return 0 ;

HBtn - это нулевой индекс "горячего" столбца заголовка при наведении мыши на заголовок

1 ответ

Я попытался отправить WM_SIZE в окно заголовка с wParam = SIZE_RESTORED и lParam = его собственным размером, и обнаружил, что строка заголовка отображается правильно.

Благодаря Реми Лебо я заменил LockWindowUpdate() на WM_SETREDRAW и улучшил производительность больших списков. Я прочитал статьи Рэймонда Чена о LockWindowUpdate() и понял, что прирост производительности WM_SETREDRAW для управления просмотром списка происходит из-за обхода "сложных вычислений экрана". С помощью подклассной процедуры некоторые вычисления должны быть переделаны. И отправка WM_SIZE выполняет ту же работу, что и изменение размера окна. Элемент управления показывает отлично без необходимости вызывать InvalidateRect()/UpdateWindow().

Примечание. Я изменил метод создания подклассов с помощью SetWindowLongPtr() на использование SetWindowSubclass(), но не оказал влияния на проблему с отображением моего заголовка. Я сортирую список по всем 20 подпунктам, поэтому использование виртуальных списков не вариант.

Другие вопросы по тегам