Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > То рисует рамку фокуса в ListView, то нет


Автор: feodorv 1.8.2013, 14:30
Уважаемые форумчане!

Может, кто сталкивался с подобной проблемой. Работаю на чистом WinAPI. Есть диалог с табом, при переключении которого отображается соответствующий ListView через ShowWindow( SW_HIDE/SW_SHOW ). Вседа работал в ListView с выбранными (SELECTED) элементами, но разрешал только один такой элемент. Теперь решил перейти на чисто фокусный элемент (FOCUSED) с тем, чтобы покрасивше можно было его оформить.

Так вот, при одном запуске одной и той же программы ListView рисуется нормально, с прерывистой рамкой у фокусного элемента ListView, а при другом запуске - эта рамка уже не отображется (ни на одном ListView). Если бы не подсветил фокусный элемент другим цветом заднего плана, то и не поймёшь, где там фокус. При этом все клавиши перемещения текущего элемента (Up/Down/PgUp etc) работают исправно, фокус перемещается.

Такое впечатление, что что-то недоинициализировано, и какая-то переменная получает случайное значение, но что именно, не могу понять.

Чуть больше информации
  • При старте программы:
    Код
      INITCOMMONCONTROLSEX ic;
      ZeroMemory( &ic, sizeof(INITCOMMONCONTROLSEX));
      ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
      ic.dwICC = ICC_WIN95_CLASSES;
      InitCommonControlsEx( &ic );
  • ListView имеет фиксированный стиль LVS_REPORT | LVS_SINGLESEL | LVS_OWNERDATA и расширенный стиль LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP
  • На ListView наложена маска обратного вызова
    Код
      ListView_SetCallbackMask( lv, LVIS_SELECTED);
    и при обработке LVN_GETDISPINFO никакому элементу не ставится флаг LVIS_SELECTED
  • В функции диалога идет обработка уведомления NM_CUSTOMDRAW:
    Код
     case NM_CUSTOMDRAW:
        {
          NMLVCUSTOMDRAW *cd = (NMLVCUSTOMDRAW *) nm;
          switch( cd->nmcd.dwDrawStage )
          {
            case CDDS_PREPAINT:
              SetWindowLong( dialog, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW);
              return TRUE;
            case CDDS_ITEMPREPAINT:
              SetWindowLong( dialog, DWL_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW);
              return TRUE;
            case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
              cd->clrText = getColor( cd->nmcd.dwItemSpec );
              if( (cd->nmcd.uItemState & CDIS_FOCUS) )
                cd->clrTextBk = RGB( 0xee, 0xee, 0xee);
              SetWindowLong( dialog, DWL_MSGRESULT, CDRF_NEWFONT);
              return TRUE;
            default:
              SetWindowLong( dialog, DWL_MSGRESULT, CDRF_DODEFAULT);
          }
          break;
        }
    и уведомления LVN_GETDISPINFO:
    Код

        case LVN_GETDISPINFO:
        {
          LVITEM *item = &((LV_DISPINFO *) nm)->item;
          if( (item->mask & LVIF_TEXT) != 0 )
            item->pszText = getText( item );
          break;
        }
    с возвратом
    Код
       return FALSE;
  • Один из симптомов проблемы - при переключении таба и дальнейшем ShowWindow( oldlv, SW_HIDE) и ShowWindow( newlv, SW_SHOW) фокус ввода остаётся на старом ListView, приходится принудительно делать SetFocus( newlv ).

Автор: feodorv 1.8.2013, 16:31
Вставил логгирование в обработчик уведомления NM_CUSTOMDRAW. Выяснил, что один и тот же элемент ListView отрисовывается дважды подряд. Естественно, рамка пропадает, поскольку DrawFocusRect второй раз просто стирает ранее нарисованную рамку:
Цитата
Because DrawFocusRect is an XOR function, calling it a second time with the same rectangle removes the rectangle from the screen. 
Но вопрос остался - почему один и тот же элемент списка ListView отрисовыватся два раза подряд...

Не прав. Когда рамка рисуется, данные тоже запрашиваются 2 раза подряд...

Автор: volatile 1.8.2013, 19:59
Вот http://social.msdn.microsoft.com/Forums/vstudio/en-US/ba894549-acfc-4113-ab62-9c4069e87654/strange-focus-rectangle-behaviour что-то похожее на вашу проблему.
Главный вывод из прочитанного: это таки обычное поведение для вендузятины (если вас это успокоит  smile ).
(подозреваю что поведение этой рамки еще слегка варьируется от версии венды)

Ну а выход... может плюнуть на эту рамку и отрисовывать что-то похоже самому?

Добавлено через 8 минут и 41 секунду
Цитата
the focus rectangle shows up if the user got there via the keyboard and not if the user got there some other way

то есть она вообще только при заходе клавиатурой должна отрисовываться. а это  не ваш случай.


Автор: feodorv 1.8.2013, 20:40
volatile, спасибо большое за ответ и ссылку smile 

Цитата(volatile @  1.8.2013,  20:59 Найти цитируемый пост)
а это  не ваш случай

Ничего, зато есть над чем подумать smile 

Цитата(volatile @  1.8.2013,  20:59 Найти цитируемый пост)
Ну а выход... может плюнуть на эту рамку и отрисовывать что-то похоже самому?

В принципе, можно жёстко диалогу UISF_HIDEFOCUS поставить, и в CDRF_NOTIFYPOSTPAINT рамку рисовать. Но как-то это неестестественно, что ли)))

Автор: feodorv 1.8.2013, 21:06
Случай, кстати, явно похожий, только более тонкий)))
Если при старте программы (когда активным становится первый ListView) в диалоге нажать два раза TAB, то прямоугольник фокуса появляется...

Автор: feodorv 1.8.2013, 21:22
Дело точно в установленном флаге UISF_HIDEFOCUS. Поставил на него проверку:
Код

    case WM_INITDIALOG:
      if( !initDialog( hWnd, (struct dialog_data_s *) lParam) ) ((struct dialog_data_s *) lParam)->success = False;
       logMessage( "DialogInit:  focus = %u", SendMessage( hWnd, WM_QUERYUISTATE, 0, 0) & UISF_HIDEFOCUS);
      return TRUE;

    case WM_DESTROY:
      logMessage( "DialogDestroy:  focus = %u", SendMessage( hWnd, WM_QUERYUISTATE, 0, 0) & UISF_HIDEFOCUS);
      finishDialog( hWnd );
      PostQuitMessage( 0 );
      SetWindowLong( hwnd, DWL_MSGRESULT, 0);
      return 0;

И что получил:
Цитата

DialogInit:  focus = 0
...
DialogDestroy:  focus = 1

Кто-то из контролов всё испортил  smile 

Вставил после initDialog в WM_INITDIALOG
Код

     SendMessage( hWnd, WM_CHANGEUISTATE, MAKEWPARAM( UIS_INITIALIZE, 0), 0);

Теперь так:
Цитата

DialogInit:  focus = 1
...
DialogDestroy:  focus = 1

 smile 

Автор: feodorv 1.8.2013, 22:00
Ну, кажись, всё  smile 
Вместо 
Код
     SendMessage( hWnd, WM_CHANGEUISTATE, MAKEWPARAM( UIS_INITIALIZE, 0), 0);
вставил
Код
    SendMessage( hWnd, WM_UPDATEUISTATE, MAKEWPARAM( UIS_CLEAR, UISF_HIDEFOCUS), 0);
и всё починилось)))


Цитата(volatile @  1.8.2013,  20:59 Найти цитируемый пост)
это таки обычное поведение для вендузятины

Да уж. Знаю по собственному опыту))) Да и в свиснутом у Microsoft коде полно комментов типа
Цитата

            //  Fix horrible undocumented hanging problem:  LVN_ENDLABELEDIT
            //  is sent in response to EN_KILLFOCUS

или
Цитата

            // Thus, as a cheap
            // and dirty solution, I check to see if the item I think I have
            // is the same one I had when I made the notify, and if not, I
            // bail.  Don't blame me, I'm just cleaning up the mess.
 smile 

Автор: feodorv 1.8.2013, 22:38
Опять не прав. Так не починилось.
Оставил WM_CHANGEUISTATE в WM_INITDIALOG, а WM_UPDATEUISTATE послал, когда исчерпалась очередь сообщений диалога.
Танцы с бубнами, forever.

Автор: volatile 2.8.2013, 11:56
feodorv, интуитивно чувтвую что - лучше все-же сделать своими руками.
Даже если вы и отладите на своей ОS, не факт что глюк не вылезет на другой.
Они же что-то там подкручивают еще. Еще и темы оформления бывают ... Как это будет там? никто знает...  smile 

В общем я бы сделал подсветку своими руками.
(Ну или клавиатурные комбинации посылайте чтоли, не знаю. но это .. smile .)

Автор: feodorv 2.8.2013, 17:14
Цитата(volatile @  2.8.2013,  12:56 Найти цитируемый пост)
Как это будет там? никто знает... 

Да, согласен, "подушка безопасности" нужна)))

Цитата(volatile @  2.8.2013,  12:56 Найти цитируемый пост)
Ну или клавиатурные комбинации посылайте чтоли, не знаю.

Думаю как раз про этот вариант smile 

Цитата(volatile @  2.8.2013,  12:56 Найти цитируемый пост)
но это ..

А чо? Нормальное решение - подождать, пока всё успокоится и нанести ответный удар smile 

Автор: volatile 2.8.2013, 23:10
Цитата(feodorv @  2.8.2013,  17:14 Найти цитируемый пост)
А чо? Нормальное решение 

ну если оно будет работать, то возможно ...

Может кто что еще посоветует...
Честно говоря, конкретно с этим, я дел не имел, просто почитал тот топик, и мне показалось
что это одно из тех многочисленных мест в венде, где мелкомягкие устроили танцы с бубном.
А это они делать умеют.
Пару раз натыкался на такие места...
Вроде бы уже все нормально, но в последний момент опять что-то вылазило ни с того, ни сего...

 smile 
А вобще писать гуй на чистом винапи... это что-то...  smile 

Автор: feodorv 3.8.2013, 00:23
Цитата(volatile @  3.8.2013,  00:10 Найти цитируемый пост)
ну если оно будет работать, то возможно ...

"Поживём - увидим" ©)))

Цитата(volatile @  3.8.2013,  00:10 Найти цитируемый пост)
Честно говоря, конкретно с этим, я дел не имел

Да не, ссылка дана хорошая, симптомы почти совпадают. Я вот найти подобное не смог, пришлось в их сурсы лезть...

Цитата(volatile @  3.8.2013,  00:10 Найти цитируемый пост)
А вобще писать гуй на чистом винапи... это что-то...

Это мазохизм!!! Даже не знаю, чем оправдаться. Но вот так сложились обстоятельства smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)