PRB: Controls in Toolbars Refuse to Give the Focus to Views

ID: Q134763


The information in this article applies to:


SYMPTOMS

In an MFC application, controls added to a toolbar may prevent the input focus from returning to the active view. For example, when a user clicks a combo box or edit control in a toolbar and then clicks a view, the control retains the input focus. This happens for any CControlBar-derived class containing a control that can accept the input focus. This behavior does not exist for views derived from CFormView or CEditView.


CAUSE

When MFC reactivates an already active view, it does not set the input focus to the view. As a result, any control in the toolbar which has the input focus retains the focus.


RESOLUTION

For Single Document Interface (SDI) applications

To overcome this behavior when you are writing a Single Document Interface (SDI) application, you must handle the case of a mouse click in the client area (the view itself) of the main window. You need to override OnMouseActivate() in your CView-derived class. To do this, be sure to add an ON_WM_MOUSEACTIVATE() entry to your message map. ClassWizard displays WM_MOUSEACTIVATE in the Messages list if you first choose the Class Info tab and change your Message Filter to "Window." In the code for OnMouseActivate(), call the base class implementation first, and save the result. If the result is MA_ACTIVATE or MA_ACTIVATEANDEAT, then call OnActivateView(). Return the result you saved from your call to the base class. The following code demonstrates this:

int CMyView::OnMouseActivate(CWnd* pDesktopWnd,
        UINT nHitTest, UINT message)
{
    // Call the base class to get a return value
    int nResult = CView::OnMouseActivate(pDesktopWnd,
            nHitTest, message);

    if (nResult == MA_ACTIVATE || nResult == MA_ACTIVATEANDEAT)
    {
         OnActivateView(TRUE, this, this);
    }

    return nResult;
} 

For Multiple Document Interface (MDI) Applications

If you are writing a Multiple Document Interface (MDI) application, there are two ways a view might be reactivated. You must handle both to overcome the focus behavior.

To handle the case of a mouse click in a non-client part of the MDI child window, override OnMouseActivate() in your CMDIChildWnd-derived class. To do this, be sure to add an ON_WM_MOUSEACTIVATE() entry to your message map. ClassWizard will display WM_MOUSEACTIVATE in the Messages list if you first choose the Class Info tab and change your Message Filter to "Window." In the code for OnMouseActivate(), call the base class implementation first, and save the result. Then call SetFocus(), and return the result you saved from your call to the base class. The following code demonstrates this:

int CMyMDIChildWnd::OnMouseActivate(CWnd* pDesktopWnd,
        UINT nHitTest, UINT message)
{
    // Call the base class FIRST to get a return value for later
    int nResult = CMDIChildWnd::OnMouseActivate(pDesktopWnd,
            nHitTest, message);

    // Correct for focus not being set in
    // CMDIChildWnd::OnMouseActivate()
    SetFocus();

    return nResult;
} 
To handle the case of choosing the already-checked item for the MDI child window from the application's Window menu, override OnCommand() for your CMainFrame class. Call the base class implementation first, and save the result. Then determine that the menu item ID you are passed is for an MDI child window, that it is your own MDI child, which should be active, and that you are of the runtime class CView. If all of that is true, then call SetFocus, and return the value you saved from your call to the base class. The following code, adapted from the MFC source for CMDIFrameWnd::OnCommand(), demonstrates how to do this:

BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
    // Call the base class FIRST to get a return value for later
    BOOL bRet = CMDIFrameWnd::OnCommand(wParam, lParam);

    ASSERT(AFX_IDM_FIRST_MDICHILD == 0xFF00);
    // If the correct part of lParam is NULL, this message was sent
    // because a menu item was chosen with the mouse or the keyboard
    if (LOWORD(lParam) == NULL &&
            (LOWORD(wParam) & 0xf000) == 0xf000)
    {
        ASSERT_VALID(MDIGetActive(NULL));
        // Do this ONLY for a CView-derived class
        if ((GetFocus()->GetParentFrame() != MDIGetActive(NULL)) &&
                (MDIGetActive(NULL)->GetActiveView()->
                IsKindOf(RUNTIME_CLASS(CView))))
        {
            SetFocus();
        }
    }

    return bRet;
} 


STATUS

This behavior is by design.


REFERENCES

For information on how to add a control, such as an edit or combo box, to a toolbar, please see the CTRLBARS sample application shipped with Microsoft Visual C++.

Additional query words: 1.50 1.51 1.52 2.50 2.51 2.52


Keywords          : kb16bitonly 
Version           : 
Platform          : 
Issue type        : 

Last Reviewed: July 30, 1999