Visual C++ Developer Center > Visual C++ Forums > Visual C++ General > My CButton class does not respond to ENTER key.
Ask a questionAsk a question
 

AnswerMy CButton class does not respond to ENTER key.

  • Tuesday, November 03, 2009 11:21 PMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi all,

    I have a custom button class that is derived from MFC CButton class. This class is owner drawn (Overriding DrawItem()). The button draws correctly, responds to mouse clicks correctly and I can TAB to it correctly as well. When in focus, the button gets activated by pressing the space bar. However, for some reason, the when the button is in focus, it does not respond/get activated when the ENTER key is pressed. I replaced my button with the MFC CButton class and it responds to ENTER key just fine when focused.

    So, does subclassing the CButton class mean I have to handle the OnGetDlgCode()/OnKeyDown()/OnChar() functions to handle the ENTER key now? Is that the case for any control that is subclassed? What am I missing here? Thanks for your help.

    P.S. I had forgotten to mention that my button is not the default button in the dialog.



    • Edited byfkhanoom Wednesday, November 04, 2009 12:38 AMClarify further
    • Edited byfkhanoom Wednesday, November 04, 2009 8:03 PMClarify further
    •  

Answers

  • Thursday, November 05, 2009 1:11 AMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    I finally figured it out. Here is my solution:

    In my custom (owner-drawn, non-default) button class (subclassed CButton class), I am intercepting the ENTER key and sending a BN_CLICKED notification code to the parent dialog via the WM_COMMAND message. The code is as follows:

    void IconButton::OnKeyDown(UINT nChar, UINT nPreCnt, UINT nFlags)
    {
       if (nChar == VK_RETURN ||
           nChar == VK_SPACE  ||
           nChar == VK_TAB)
       {
          // Handle this in OnChar()
          return ;
       }

       return CButton::OnKeyDown(nChar, nPreCnt, nFlags);
    }

    void IconButton::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
       switch (nChar)
       {
       case VK_RETURN:
       case VK_SPACE:
          // Handle the ENTER and SPACEBAR keys.
          GetParent()->SendMessage(
             WM_COMMAND,
             MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED),
             (LPARAM) GetSafeHwnd());
          break;

       case VK_TAB:
          GetParent()->SendMessage(WM_NEXTDLGCTL, 0, 0);
          break;

       default:
          // Handle the rest.
          CButton::OnChar(nChar, nRepCnt, nFlags);
       }

       return ;
    }

    UINT IconButton::OnGetDlgCode()
    {
       // Intercept all keyboard inputs. We don't get the option to just intercept the ENTER key. Thus,
      // in OnChar/OnKeyDown we are forced to handle all keyboard inputs that are relevant to the button.
       return CButton::OnGetDlgCode() | DLGC_WANTMESSAGE;
    }

    In my dialog class, I added the ON_BN_CLICKED to the map entry to handle the button click:

    BEGIN_MESSAGE_MAP
    ON_BN_CLICKED(myButtonID, myButtonClicked) 
    END_MESSAGE_MAP

    I already had the myButtonClicked function previously for mouse clicks, etc. But it also now works when the user presses ENTER on my button (when it is in focus) or the SPACEBAR. Hope this helps.

    Any further comments, opinions are welcome. Thanks.



    • Marked As Answer byfkhanoom Thursday, November 05, 2009 1:11 AM
    •  

All Replies

  • Wednesday, November 04, 2009 4:57 AMJangid Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Set default button property true or modify button style to BS_DEFPUSHBUTTON

    or try CDialog::SetDefID function

    Kind Regards
    Manoj Jangid

    मनोज कुमार जांगिड NATHCORP
    • Proposed As Answer byJangid Wednesday, November 04, 2009 4:57 AM
    • Unproposed As Answer byfkhanoom Thursday, November 05, 2009 1:13 AM
    •  
  • Wednesday, November 04, 2009 10:02 AMhgn Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    This is one of the mysteries of ::IsDialogMessage(). If VK_RETURN is pressed normal buttons get special treatment. But buttons with the BS_OWNERDRAW style do not. You can exclude VK_RETURN from beeing handled by ::IsDialogMessage() and handle that key your self this way:

    BOOL CYourDialog::PreTranslateMessage(MSG* pMsg)
    {
       switch ( pMsg->message )
       {
          case WM_KEYDOWN:
          {
             switch( pMsg->wParam )
             {
                case VK_RETURN:
                   ::DispatchMessage( pMsg );
                   return true;
             }
             break;
          }
       }
       return CDialog::PreTranslateMessage(pMsg);
    }
     

  • Wednesday, November 04, 2009 5:18 PMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I would like my button class to be reusable. Therefore, I rather handle it in the subclassed button class rather than in my dialog(s). In that case, I should probably override OnGetDlgCode()/OnKeyDown()/OnChar() functions in my button class to handle the ENTER key.
  • Wednesday, November 04, 2009 5:46 PMNikita Leontiev Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Your button is located on dialog?
  • Wednesday, November 04, 2009 5:53 PMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Yes it's located on a dialog.
  • Wednesday, November 04, 2009 6:07 PMNikita Leontiev Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Have you seen this thread?

  • Wednesday, November 04, 2009 6:20 PMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi Nikita,

    I saw the question in that thread but you have to pay/provide some info to get the answer. If you have access to the thread answer, could you post it here?

     I tried the previous two solutions provided here and they did not work.

     I did find an article that explains what to do to make an owner drawn button to behave correctly, mind you it's a lengthy process and I am yet to try it: http://www.codeproject.com/KB/buttons/oddbutton.aspx?msg=80296.
  • Wednesday, November 04, 2009 6:33 PMNikita Leontiev Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    You just need to scroll site to the bottom and you will see answers. It's EE site trick =)
  • Wednesday, November 04, 2009 6:35 PMNikita Leontiev Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Also, I think that codeproject article is the best solution for your problem, IMHO
  • Wednesday, November 04, 2009 8:01 PMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I am not sure if the codeproject article handles a non-default owner-drawn button case. I should have mentioned in my problem description that my button is not the dialog's default button.
  • Wednesday, November 04, 2009 8:18 PMNikita Leontiev Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Yes, it uses default button technique.
    Also, I've tested sample from code project article and it works incorrectly =)
  • Wednesday, November 04, 2009 8:48 PMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I better use Spy++ to see what the difference between a standard and owner-drawn button message map is. I don't see any other way :(
  • Thursday, November 05, 2009 1:11 AMfkhanoom Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    I finally figured it out. Here is my solution:

    In my custom (owner-drawn, non-default) button class (subclassed CButton class), I am intercepting the ENTER key and sending a BN_CLICKED notification code to the parent dialog via the WM_COMMAND message. The code is as follows:

    void IconButton::OnKeyDown(UINT nChar, UINT nPreCnt, UINT nFlags)
    {
       if (nChar == VK_RETURN ||
           nChar == VK_SPACE  ||
           nChar == VK_TAB)
       {
          // Handle this in OnChar()
          return ;
       }

       return CButton::OnKeyDown(nChar, nPreCnt, nFlags);
    }

    void IconButton::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
       switch (nChar)
       {
       case VK_RETURN:
       case VK_SPACE:
          // Handle the ENTER and SPACEBAR keys.
          GetParent()->SendMessage(
             WM_COMMAND,
             MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED),
             (LPARAM) GetSafeHwnd());
          break;

       case VK_TAB:
          GetParent()->SendMessage(WM_NEXTDLGCTL, 0, 0);
          break;

       default:
          // Handle the rest.
          CButton::OnChar(nChar, nRepCnt, nFlags);
       }

       return ;
    }

    UINT IconButton::OnGetDlgCode()
    {
       // Intercept all keyboard inputs. We don't get the option to just intercept the ENTER key. Thus,
      // in OnChar/OnKeyDown we are forced to handle all keyboard inputs that are relevant to the button.
       return CButton::OnGetDlgCode() | DLGC_WANTMESSAGE;
    }

    In my dialog class, I added the ON_BN_CLICKED to the map entry to handle the button click:

    BEGIN_MESSAGE_MAP
    ON_BN_CLICKED(myButtonID, myButtonClicked) 
    END_MESSAGE_MAP

    I already had the myButtonClicked function previously for mouse clicks, etc. But it also now works when the user presses ENTER on my button (when it is in focus) or the SPACEBAR. Hope this helps.

    Any further comments, opinions are welcome. Thanks.



    • Marked As Answer byfkhanoom Thursday, November 05, 2009 1:11 AM
    •