Virtual ListCtrl и OwnerDraw
От: Captain_Blood  
Дата: 28.09.04 14:09
Оценка:
Доброе время суток, многоуважаемый Олл.

Проблема: приложение содержит ListCtrl. Поскольку требуется показывать число записей от нескольких штук до нескольких десятков тысяч, я создаю его, как virtual ListCtrl. Данные в определенных колонках должны показываться в графической форме, и я использую для этих колонок Custom Draw.

Все работает превосходно (и даже более чем), пока список показывает действительно большое число записей. Как только число записей оказывается меньше, чем 21 (такое странное магическое число , я начинаю получать событие _OnCustomDraw с неверными индексами item и subitem, подлежащими отрисовке. То есть:

void CMyView::OnCustomDrawList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NMLVCUSTOMDRAW* pLVCustomDraw = (NMLVCUSTOMDRAW*) pNMHDR;

TRACE("pLVCustomDraw->nmcd.dwItemSpec = %d; pLVCustomDraw->iSubItem = %d\n", pLVCustomDraw->nmcd.dwItemSpec, pLVCustomDraw->iSubItem);
TRACE("Total number of items = %d\n", m_List.GetItemCount());

... здесь код, выполняющий перерисовку ...


}

В окне Outputs печатаются следующие строки:

pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1342888; pLVCustomDraw->iSubItem = 1243748; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1342888; pLVCustomDraw->iSubItem = 1243748; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 20; pLVCustomDraw->iSubItem = 1243644; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 20; pLVCustomDraw->iSubItem = 1243644; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20
pLVCustomDraw->nmcd.dwItemSpec = 1243632; pLVCustomDraw->iSubItem = 1; Total number of items = 20

Иногда вместо индекса item или subitem приходит вообще отрицательное число. Я пробовал для интереса представить все эти числа в шестнадцатеричном формате, но скрытого смысла в них не нашел . Как только число записей превышает 20, я начинаю получать правильные индексы.

Я думаю, что проблема в виртуальной природе этого ListCtrl, поскольку в другом месте того же приложения я использую ListCtrl только с CustomDraw. В этом ListCtrl все индексы нормальные, даже если список содержит всего 1 или 2 элемента.

Я работаю с Visual Studio 6.0 + SP5 в Windows 2000 Pro + SP4.

Любые разумные идеи по устранению грабель будут восприняты с благодарностью.
Re: Virtual ListCtrl и OwnerDraw
От: Аноним  
Дата: 29.09.04 20:18
Оценка:
Здравствуйте, Captain_Blood, Вы писали:

C_B>void CMyView::OnCustomDrawList1(NMHDR* pNMHDR, LRESULT* pResult)

C_B>{
C_B> NMLVCUSTOMDRAW* pLVCustomDraw = (NMLVCUSTOMDRAW*) pNMHDR;

C_B> TRACE("pLVCustomDraw->nmcd.dwItemSpec = %d; pLVCustomDraw->iSubItem = %d\n", pLVCustomDraw->nmcd.dwItemSpec, pLVCustomDraw->iSubItem);

C_B> TRACE("Total number of items = %d\n", m_List.GetItemCount());

C_B> ... здесь код, выполняющий перерисовку ...



C_B>}


Эээ..., из приведённого кода не видно, как Вы обрабатываете dwDrawStage...

Скажем такой вариант правильно выдаёт: ?
switch ( pNMCD->nmcd.dwDrawStage )
{
case CDDS_PREPAINT:
    {
        *pResult = CDRF_NOTIFYITEMDRAW;
        break;
    }
case CDDS_ITEMPREPAINT:
    {
        TRACE("Item %d, SubItem %d\n", pNMCD->nmcd.dwItemSpec, pNMCD->iSubItem);
        *pResult = CDRF_SKIPDEFAULT;
        break;
    }
default:
    {
        *pResult = 0;
        break;
    }
}
Re[2]: Virtual ListCtrl и OwnerDraw
От: Captain_Blood  
Дата: 03.10.04 06:43
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Эээ..., из приведённого кода не видно, как Вы обрабатываете dwDrawStage...


А>Скажем такой вариант правильно выдаёт: ?

А>
А>switch ( pNMCD->nmcd.dwDrawStage )
А>{
А>case CDDS_PREPAINT:
А>    {
А>        *pResult = CDRF_NOTIFYITEMDRAW;
А>        break;
А>    }
А>case CDDS_ITEMPREPAINT:
А>    {
А>        TRACE("Item %d, SubItem %d\n", pNMCD->nmcd.dwItemSpec, pNMCD->iSubItem);
А>        *pResult = CDRF_SKIPDEFAULT;
А>        break;
А>    }
А>default:
А>    {
А>        *pResult = 0;
А>        break;
А>    }
А>}
А>


Ну в принципе, я то же самое делаю. Вот код:


if (pLVCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT)
{
    // Это, чтобы получать нотификацию CustomDraw

    *pResult = CDRF_NOTIFYITEMDRAW;
}

else if (pLVCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
{
    // Это присылается на нулевую колонку (то есть собственно item). Здесь
    // меня устраивает отрисовка по умолчанию, поэтому я возвращаю CDRF_DODEFAULT 
    // и заказываю нотификацию о перерисовке subitems через возврат CDRF_NOTIFYSUBITEMDRAW

    *pResult = CDRF_DODEFAULT | CDRF_NOTIFYSUBITEMDRAW;
}

else if ((pLVCustomDraw->nmcd.dwDrawStage & CDDS_SUBITEM) == CDDS_SUBITEM)
{
    // А это присылается на каждую колонку (то есть subitem). Две из них я 
    // рисую сам, а остальные - по умолчанию

    switch (pLVCustomDraw->iSubItem)
    {
        case COL_GRAPHIC1:

            ... тут рисуем ...
                
            *pResult = CDRF_SKIPDEFAULT | CDRF_NOTIFYSUBITEMDRAW;
            break;

        case COL_GRAPHIC2:

            ... и тут рисуем ...

            *pResult = CDRF_SKIPDEFAULT | CDRF_NOTIFYSUBITEMDRAW;
            break;

        default:

            ... а эти (все остальные) ListCtrl рисует сам ...

            *pResult = CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYSUBITEMDRAW;
            break;
    }

}

else if (pLVCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT)
{
    // А тут освобождаются некоторые ресурсы (типа шрифтов)
}
Re[3]: Virtual ListCtrl и OwnerDraw
От: Аноним  
Дата: 03.10.04 20:31
Оценка:
Здравствуйте, Captain_Blood, Вы писали:


C_B>Ну в принципе, я то же самое делаю. Вот код:


C_B>
C_B>else if (pLVCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
C_B>{
C_B>    // Это присылается на нулевую колонку (то есть собственно item). Здесь
C_B>    // меня устраивает отрисовка по умолчанию, поэтому я возвращаю CDRF_DODEFAULT 
C_B>    // и заказываю нотификацию о перерисовке subitems через возврат CDRF_NOTIFYSUBITEMDRAW

C_B>    *pResult = CDRF_DODEFAULT | CDRF_NOTIFYSUBITEMDRAW;
C_B>}


Как-то это не совсем корректно выглядит в свете:

MSDN CDRF_DODEFAULT:
The control will draw itself. It will not send any additional NM_CUSTOMDRAW messages for this paint cycle. This occurs when dwDrawState equals CDDS_PREPAINT.


и ещё
C_B>else if ((pLVCustomDraw->nmcd.dwDrawStage & CDDS_SUBITEM) == CDDS_SUBITEM)


MSDN CDRF_NOTIFYSUBITEMDRAW:
Version 4.71. Your application will receive an NM_CUSTOMDRAW message with dwDrawState set to CDDS_ITEMPREPAINT | CDDS_SUBITEM before each list-view subitem is drawn


Xотя, приведённый Вами код успешно работает, правда на VS7.1.
Если уж такая проблема, может стоит попробовать, воспроизвести подобное на новом чистом проекте, по самому простому варианту?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.