Пользовательский чертеж элемента управления — состояние фокусировки

#c #winapi #mfc #focus

#c #winapi #mfc #фокус

Вопрос:

Я создал два экземпляра класса CHoverButtonEx от Фреда Акера с небольшой модификацией, чтобы включить отключенное состояние.

Эти кнопки существуют в немодальном диалоговом окне, которое содержит следующие свойства:

 IDD_MY_DIALOG DIALOGEX 0, 0, 162, 27
STYLE DS_SETFONT | WS_POPUP
EXSTYLE WS_EX_TOPMOST | WS_EX_TOOLWINDOW
FONT 9, "Arial", 400, 0, 0x0
BEGIN
    CONTROL         146,IDC_STATIC_BKGND,"Static",SS_BITMAP,0,0,162,27
    LTEXT           "",IDC_STATIC_1,6,4,101,9,SS_WORDELLIPSIS
    LTEXT           "",IDC_STATIC_2,6,15,101,9
    CONTROL         "",IDC_BUTTON_1,"Button",BS_OWNERDRAW | WS_TABSTOP,108,4,24,19
    CONTROL         "",IDC_BUTTON_2,"Button",BS_OWNERDRAW | WS_TABSTOP,134,4,24,19
END
  

С кнопками все работает хорошо, за исключением того, что теперь мне нужно реализовать состояние фокусировки, но поведение странное и неожиданное.

В моем обработчике сообщений DrawItem у меня есть следующий код, который функционирует практически точно так же, как оригинал, за вычетом некоторых вещей, которые я очистил, которые не были нужны:

 void CHoverButtonEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
// Do other stuff above and now find the state and draw the bitmap

if(lpDrawItemStruct->itemState amp; ODS_SELECTED)
{
   //mydc->BitBlt(0,0,m_ButtonSize.cx,m_ButtonSize.cy,pMemDC,m_ButtonSize.cx,0,SRCCOPY);
   mydc->StretchBlt(0,0, lpDrawItemStruct->rcItem.right-lpDrawItemStruct->rcItem.left,
       lpDrawItemStruct->rcItem.bottom-lpDrawItemStruct->rcItem.top,
       pMemDC,m_ButtonSize.cx,0, m_ButtonSize.cx,m_ButtonSize.cy, SRCCOPY );
}
else
{
    if(m_bHover)
    {
       //mydc->BitBlt(0,0,m_ButtonSize.cx,m_ButtonSize.cy,pMemDC,m_ButtonSize.cx*2,0,SRCCOPY);
       mydc->StretchBlt(0,0, lpDrawItemStruct->rcItem.right-lpDrawItemStruct->rcItem.left,
           lpDrawItemStruct->rcItem.bottom-lpDrawItemStruct->rcItem.top,
           pMemDC,m_ButtonSize.cx*2,0, m_ButtonSize.cx,m_ButtonSize.cy, SRCCOPY );
    }
    else
    {
        if (IsWindowEnabled())
        {
           //mydc->BitBlt(0,0,m_ButtonSize.cx,m_ButtonSize.cy,pMemDC,0,0,SRCCOPY);
           mydc->StretchBlt(0,0, lpDrawItemStruct->rcItem.right-lpDrawItemStruct->rcItem.left,
               lpDrawItemStruct->rcItem.bottom-lpDrawItemStruct->rcItem.top,
               pMemDC,0,0, m_ButtonSize.cx,m_ButtonSize.cy, SRCCOPY );
        }
        else
        {
           //mydc->BitBlt(0,0,m_ButtonSize.cx,m_ButtonSize.cy,pMemDC,m_ButtonSize.cx*3,0,SRCCOPY);
           mydc->StretchBlt(0,0, lpDrawItemStruct->rcItem.right-lpDrawItemStruct->rcItem.left,
               lpDrawItemStruct->rcItem.bottom-lpDrawItemStruct->rcItem.top,
               pMemDC,m_ButtonSize.cx*3,0, m_ButtonSize.cx,m_ButtonSize.cy, SRCCOPY );
        }
    }
}

if (lpDrawItemStruct->itemAction amp; ODA_FOCUS)
{
   RECT rcFocus;
   int iChange = 3;
   rcFocus.top = lpDrawItemStruct->rcItem.top   iChange;
   rcFocus.left = lpDrawItemStruct->rcItem.left   iChange;
   rcFocus.right = lpDrawItemStruct->rcItem.right - iChange;
   rcFocus.bottom = lpDrawItemStruct->rcItem.bottom - iChange;
   pMemDC->DrawFocusRect(amp;rcFocus);
}

// clean up
pMemDC -> SelectObject(pOldBitmap);
delete pMemDC;
}
  

Происходит то, что, когда диалоговое окно является активным окном, и я нажимаю tab один раз, поле фокусировки переходит ко второй кнопке, хотя я могу подтвердить через обработчик нажатия кнопки, что первая кнопка имеет реальный фокус. Затем, когда я снова нажимаю tab, поле фокусировки переключается, чтобы включить обе кнопки. Затем другое нажатие табуляции перемещает поле фокусировки на другую кнопку и, наконец, еще одно нажатие табуляции полностью удаляет поле фокусировки. Эта последовательность продолжает происходить. Даже удерживание Shift-Tab не повлияет на это.

Я проверил сообщения Windows с помощью spy , и это выглядит довольно нормально. Я получаю сообщение WM_DRAWITEM для обоих элементов управления кнопками, и они успешно обработаны.

Я упомяну одну последнюю вещь; в моем диалоговом коде, когда я инициализирую кнопки, я вынужден размещать кнопки в нижней части z-порядка, иначе IDC_STATIC_BKGND будет рисовать поверх кнопок. Мне это не показалось нормальным, потому что они уже должны быть в нижней части z-порядка. (Просто добавляю его на случай, если это часть причины моей проблемы).

 m_button1.SetHorizontal(true);
m_button1.SetMoveable(FALSE);
m_button1.LoadBitmap(IDB_BUTTON_1);
m_button1.SetToolTipText(_T("Some Text1"));
// Draws the button after the background is drawn
m_button1.SetWindowPos(amp;CWnd::wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

m_button2.SetHorizontal(true);
m_button2.SetMoveable(FALSE);
m_button2.LoadBitmap(IDB_BUTTON_2);
m_button2.SetToolTipText(_T("Some Text2"));
// Draws the button after the background is drawn
m_button2.SetWindowPos(amp;CWnd::wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
  

Кто-нибудь знает, как правильно добавить поле фокусировки для моей ситуации?

Спасибо.

Обновление: Попробовав предложение BrendanMcK и не решив проблему, я еще немного покопался в захваченных сообщениях в spy и заметил нечто, что кажется странным поведением. Следующие сообщения представляют собой одно нажатие вкладки в диалоговом окне.

 <00283> 00870794 R WM_CTLCOLORBTN hBrush:01900015
<00284> 00870794 S WM_DRAWITEM idCtl:112 lpdis:002AEF2C
<00285> 00870794 R WM_DRAWITEM fProcessed:True
<00286> 00870794 S DM_GETDEFID
<00287> 00870794 R DM_GETDEFID wHasDef:DC_HASDEFID wDefID:0001
<00288> 00870794 S WM_CTLCOLORBTN hdcButton:110114A7 hwndButton:01090502
<00289> 00870794 R WM_CTLCOLORBTN hBrush:01900015
<00290> 00870794 S WM_DRAWITEM idCtl:113 lpdis:002AF2A0
<00291> 00870794 R WM_DRAWITEM fProcessed:True
  

Как видно, доставлены два отдельных сообщения WM_DRAWITEM. Подробности для каждого сообщения:

Сообщение # 284: действие = ODA_FOCUS, состояние = 0x0110 (ODS_FOCUS = 0x0010)

Сообщение # 290: действие = ODA_DRAWENTIRE, состояние= ODS_NOACCEL

Я бы ожидал, что в сообщении # 290 действие снова будет ODA_FOCUS, чтобы разрешить другой кнопке «отменить рисование» поля фокусировки.

Я не уверен, почему я даже получаю состояние ODS_NOACCEL, поскольку я использую Win7. Есть ли что-то, что я забыл отключить?

Ответ №1:

Из MSDN в DRAWITEMSTATE:

ODA_FOCUS Элемент управления потерял или получил фокус клавиатуры. Следует проверить элемент ItemState, чтобы определить, имеет ли элемент управления фокус.

Поскольку вы перерисовываете элемент управления, вы должны рисовать прямую линию фокуса только в том случае, если состояние элемента указывает, что элемент управления имеет фокус. Вместо этого вы рисуете его во всех случаях, независимо от того, получает ли элемент управления или теряет фокус. Добавьте проверку, чтобы увидеть, есть ли ItemState amp; ODS_FOCUS, и все должно быть хорошо.

Комментарии:

1. спасибо за ваше предложение, но не повезло. Я добавил обновление к основному сообщению.