Раскраска строк ListView на Ассемблере

Элемент диалоговых окон ListView (или SysListView32) - очень удобный элемент, особенно для табличного отображения данных или ведения логов. При всем своем удобстве, у него есть недостаток, что при заполнении списка нельзя указывать цвета текста и фона для отдельных строк. Но способ раскрасить строки ListView все-таки есть. При отрисовке списка окну приложения посылается сообщение WM_NOTIFY, которое можно перехватить и самостоятельно обработать. В lParam сообщения передается указатель на структуру NMLVCUSTOMDRAW, в которой и описаны все подробности об отрисовываемой строке. Это составная структура и ее составляющие зависят от версии библиотеки Comctl32, но описываемый способ не сработает разве что на Windows 95, если вы его где-то откопаете. Однако компилятор FASM тоже ни сном, ни духом про структуру NMLVCUSTOMDRAW, придется сделать описание самостоятельно:
; Структура для обработки сообщений
struct NMLVCUSTOMDRAW
        ; NMHDR
        hwndFrom        dd ?
        idFrom          dd ?
        code            dd ?
 
        ; NMCUSTOMDRAWINFO
        dwDrawStage     dd ?
        hdc             dd ?
        left            dd ?
        top             dd ?
        right           dd ?
        bottom          dd ?
        dwItemSpec      dd ?
        uItemState      dd ?
        lItemlParam     dd ?
 
        ; NMLVCUSTOMDRAW
        clrText         dd ?
        clrTextBk       dd ?
ends

Теперь разберем значения полей структуры. В hwndFrom должен быть хэндл списка ListView, вcode - уведомление NM_CUSTOMDRAW. Если эти два условия выполняются, то требуется задействовать наш обработчик. А вот дальше есть важные особенности. Отрисовка строки выполняется в два этапа: сперва в dwDrawStage приходит уведомление CDDS_PREPAINT, на которое окно приложения должно ответить установкой статуса CDRF_NOTIFYITEMDRAW, то есть надо сообщить системе, что мы хотим самостоятельно внести коррективы в процесс отрисовки. В этом случае система посылает еще одно уведомление - CDDS_ITEMPREPAINT, и именно на этом втором этапе мы можем указать цвет фона и текста для строки. Цвет текста задается в clrText, а цвет фона - в clrTextBk. Весь этот процесс описан в MSDN, но как-то слишком разрозненно.

После теоретической подготовки переходим к практике. Сперва сделаем список с раскраской "зеброй", то есть чередование светлого и более темного фона строк. Это значительно повышает наглядность, особенно на широких таблицах. В параметре dwItemSpec структурыNMLVCUSTOMDRAW передается индекс (порядковый номер) строки. Проверяем его, и, если индекс четный, то устанавливаем один фон, если нечетный - другой. Обработчик WM_NOTIFYдля окна ListView будет выглядеть примерно так:
        ; Сообщение NM_CUSTOMDRAW?
        cmp     dword [eax+nml.code],NM_CUSTOMDRAW
        jne     .processed
 
        ; Пред-отрисовка строки?
        cmp     dword [eax+nml.dwDrawStage],CDDS_PREPAINT
        jne     @f
 
        ; Установить ответ окна
        invoke  SetWindowLong,[hwnddlg],DWL_MSGRESULT,CDRF_NOTIFYITEMDRAW
        mov     eax,CDRF_NOTIFYITEMDRAW
        jmp     .processed
@@:
        ; Требуется нарисовать строку?
        cmp     dword [eax+nml.dwDrawStage],CDDS_ITEMPREPAINT
        jne     @f
 
        ; Цвет фона нечетных строчек
        mov     esi,0E0E0E0h
        ; Проверить индекс строки на четность
        test    dword [eax+nml.dwItemSpec],1
        jnz     .row_odd
 
        ; Цвет фона четных строчек
        mov     esi,0FFFFFFh
.row_odd:
        ; Установить цвет текста строки
        mov     dword [eax+nml.clrText],0
        ; Установить цвет фона
        mov     dword [eax+nml.clrTextBk],esi
@@:
        jmp     .processed

Как видите, ничего сложного. Дополнительный плюс в том, что при добавлении или удалении строчек в списке, его раскраска "зеброй" будет каждый раз автоматически пересчитываться и правильно перерисовываться. Но этот же плюс выходит боком в случае, если индивидуальная раскраска требуется только для отдельных строчек списка. При прокручивании или обновлении окна списка вызывается его перерисовка, поэтому раскраску каждой строки придется хранить где-нибудь в памяти и обрабатывать индивидуально. Разберем это на более сложном примере, теперь строки списка будут раскрашены в случайные цвета, значения которых будут храниться в массиве.
section '.data' data readable writeable
...
colors  rd      1000    ; Массив фоновых цветов строк
...

При заполнении списка сохраним в массиве цвет каждой строки:
        ; Заполнить список строчками
        xor     ebx,ebx
@@:
        ...
        ; Тут вызывается какая-нибудь функция генерации случайного числа,
        ; результат которой записывается в EAX
        ...
 
        ; Сохранить цвет фона в массиве
        mov     ecx,ebx
        shl     ecx,2
        and     eax,0FFFFFFh
        mov     [colors+ecx],eax
 
        ...
        ; Добавляем строку в список
        ...
 
        inc     ebx
        cmp     ebx,1000
        jb      @b

Обработчик WM_NOTIFY для окна ListView немного меняется. Теперь цвет фона не зависит от четности строки, а берется из массива цветов по номеру строки. Для простоты цвет текста берется инверсным к цвету фона, в реальном проекте можно также задавать его при добавлении строк и хранить в соответствующем массиве.
        ; Сообщение NM_CUSTOMDRAW?
        cmp     dword [eax+nml.code],NM_CUSTOMDRAW
        jne     .processed
 
        ; Пред-отрисовка строки?
        cmp     dword [eax+nml.dwDrawStage],CDDS_PREPAINT
        jne     @f
 
        ; Установить ответ окна
        invoke  SetWindowLong,[hwnddlg],DWL_MSGRESULT,CDRF_NOTIFYITEMDRAW
        mov     eax,CDRF_NOTIFYITEMDRAW
        jmp     .processed
@@:
        ; Требуется нарисовать строку?
        cmp     dword [eax+nml.dwDrawStage],CDDS_ITEMPREPAINT
        jne     @f
 
        ; Получить номер строки
        mov     ebx,dword [eax+nml.dwItemSpec]
        ; Вычислить индекс массива фоновых цветов
        shl     ebx,2
        mov     esi,[colors+ebx]
        ; Вычислить текст текста - инверсный от цвета фона
        mov     edi,esi
        not     edi
        and     edi,0FFFFFFh
 
        ; Установить цвет текста строки
        mov     dword [eax+nml.clrText],edi
        ; Установить цвет фона
        mov     dword [eax+nml.clrTextBk],esi
@@:
        jmp     .processed

Некоторая сложность этого метода заключается в том, что при добавлении или удалении строчек в списке обязательно требуется вносить изменения и в массив цветов, также удаляя или добавляя в него нужные значения.

В приложении две программы с исходными тестами, реализующие оба описанных в статье способа. Первая программа создает список, раскрашенный "зеброй", двойной клик удаляет выбранную строку из списка. Вторая программа создает список, в котором строки раскрашены случайным образом.
Теги:
ListView
Добавлено: 10 Апреля 2018 17:54:47 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...