0
votes

I've created two instances of Fred Acker's CHoverButtonEx class with a slight modification to include a disabled state.

These buttons exist on modeless dialog which contains the following properties:

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

Everything is working well with the buttons except that now I need to implement a focus state but the behavior is strange and unexpected.

In my DrawItem message handler, I have the following code which functions pretty much exactly the same as the original minus some stuff I cleaned out which wasn't needed:

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

if(lpDrawItemStruct->itemState & 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 & 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(&rcFocus);
}

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

What is occurring is that when the dialog is the active window and I press tab once, the focus box jumps to the second button, even though I can confirm through the button's click handler that the first button has the real focus. Then when I press tab again the focus box jumps to include both buttons. Then another tab press moves the focus box to the other button and finally another tab press removes the focus box entirely. This sequence keeps occurring. Even holding Shift-Tab won't affect it.

I sniffed out the windows messages using spy++ and it looks pretty normal. I get a WM_DRAWITEM message for both button controls and they are successfully handled.

I'll mention one last thing; in my dialog code, when I'm initializing the buttons, I'm forced to place the buttons in the bottom of the z-order or else the IDC_STATIC_BKGND would draw over the buttons. This didn't seem normal to me because they should already be at the bottom of the z-order. (Just adding it in case it's a part of the cause to my problem).

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(&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(&CWnd::wndBottom, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

Does anyone know how to correctly add a focus box for my situation?

Thanks.

Update: After trying BrendanMcK's suggestion and not resolving the issue, I dug some more in the captured messages in spy++ and noticed something that seems like odd behavior. The following messages represent a single tab press in the dialog.

<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

As evident there are two separate WM_DRAWITEM messages delivered. The details for each message is:

Message #284: action= ODA_FOCUS, state= 0x0110 (ODS_FOCUS = 0x0010)

Message #290: action= ODA_DRAWENTIRE, state= ODS_NOACCEL

I would have expected that in message #290, the action would again be ODA_FOCUS to allow the other button to "un-draw" the focus box.

I'm not sure why I'm even receiving an ODS_NOACCEL state as I'm using Win7. Is there something I've forgot to disable?

1

1 Answers

0
votes

From MSDN on DRAWITEMSTATE:

ODA_FOCUS The control has lost or gained the keyboard focus. The itemState member should be checked to determine whether the control has the focus.

Because you're redrawing the control, you should only be drawing the focus rect if the itemState indicates that the control has focus. Instead, you're drawing it in all cases, regardles of whether the control gains or loses focus. Add a check to see if itemState & ODS_FOCUS and you should be good.