WM_SETREDRAW блокирует отображение нарисованного владельцем заголовка списка
Я следовал совету по использованию WM_SETREDRAW вместо LockWindowUpdate () для подавления обновления списка при заполнении элементов. Я обнаружил, что моя строка заголовка не отображается после отправки сообщения WM_SETREDRAW с true. Комбинации InvalidateRect() и UpdateWindow() или RedrawWindow() не могут отобразить заголовок, нарисованный владельцем. Чтобы показать строку заголовка, мне нужно вручную форсировать новое сообщение WM_PAINT путем изменения размера или прокрутки экрана просмотра списка. Такое поведение ограничено владельцем нарисованного заголовка. Если я не подклассифицирую процедуру заголовка, процедура родного окна просто отображает строку заголовка после того, как сообщение WM_SETREDRAW отправлено с true. Кто-нибудь может подсказать, чего не хватает в моем коде?
Заметки:
- Использование WM_SETREDRAW оказалось гораздо более эффективным, чем LockWindowUpdae(). Для большого списка заполнение элементов и их отображение заняли 1,9 с, а не 9,6 с при использовании LockWindowUpdate(). Я надеюсь, что смогу правильно отобразить заголовок с помощью WM_SETREDRAW, чтобы повысить производительность.
- При создании списка без добавления элементов (и без отправки сообщения 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 подпунктам, поэтому использование виртуальных списков не вариант.