Доброе время суток, многоуважаемый Олл.
Проблема: приложение содержит 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.
Любые разумные идеи по устранению грабель будут восприняты с благодарностью.
Здравствуйте, 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;
}
}
Здравствуйте, Аноним, Вы писали:
А>Эээ..., из приведённого кода не видно, как Вы обрабатываете 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)
{
// А тут освобождаются некоторые ресурсы (типа шрифтов)
}
Здравствуйте, 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.
Если уж такая проблема, может стоит попробовать, воспроизвести подобное на новом чистом проекте, по самому простому варианту?