Summary
Windows-based applications often display data-entry dialog boxes to request information from users. These dialog boxes may contain several edit controls and two command (push) buttons, labeled OK and CANCEL. An example of a data-entry dialog box is one that requests personal information, such as social security number, address, identification (ID) number, date/time, and so on, from users. Each of these items is entered into an edit control.
By default, the TAB key is used in a dialog box to shift focus between edit controls. As a common user-interface, however, one could also use the ENTER (RETURN) key to move between the edit controls (for example, after the user enters a piece of information, pressing ENTER moves the focus to the next field). There are a few ways to enable the use of the ENTER key to move between edit controls. One method is to make use of WM_COMMAND and the notification messages that come with it in the dialog box for edit controls and buttons. Another method involves subclassing the edit controls. A third involves using App Studio and Class Wizard and creating a new dialog box member function.More Information
Method I: (WM_COMMAND)
This method is based on the following behavior of dialog boxes (Dialog Manager) and focus handling in Windows.
If a dialog box or one of its controls currently has the input focus, then pressing the ENTER key causes Windows to send a WM_COMMAND message with the idItem (wParam) parameter set to the ID of the default command button. If the dialog box does not have a default command button, then the idItem parameter is set to IDOK by default. When an application receives the WM_COMMAND message with idItem set to the ID of the default command button, the focus remains with the control that had the focus before the ENTER key was pressed. Calling GetFocus() at this point returns the handle of the control that had the focus before the ENTER key was pressed. The application can check this control handle and determine whether it belongs to any of the edit controls in the dialog box. If it does, then the user was entering data into one of the edit controls and after doing so, pressed ENTER. At this point, the application can send the WM_NEXTDLGCTL message to the dialog box to move the focus to the next control. However, if the focus was with one of the command buttons (CANCEL or OK), then GetFocus() returns a button control handle, at which point one can dismiss the dialog box. The pseudo code for this logic resembles the following in the application's dialog box procedure: case WM_COMMAND:
if(wParam=IDOFDEFBUTTON || IDOK) { // User has hit the ENTER key. hwndTest = GetFocus() ; retVal = TesthWnd(hWndTest) ; //Where retVal is a boolean variable that indicates whether //the hwndTest is the handle of one of the edit controls. if(hwndTest) { //Focus is with an edit control, so do not close the dialog. //Move focus to the next control in the dialog. PostMessage(hDlg, WM_NEXTDLGCTL, 0, 0L) ; return TRUE ; } else { //Focus is with the default button, so close the dialog. EndDialog(hDlg, TRUE) ; return FALSE ; } } break ;
Method II
This method involves subclassing/superclassing the edit control in the dialog box. Once the edit controls are subclassed or superclassed, all keyboard input is sent the subclass/superclass procedure of the edit control that currently has input focus, regardless of whether or not the dialog box has a default command button. The application can trap the key down (or char) messages, look for the ENTER key, and do the processing accordingly. The following is a sample subclass procedure that looks for the ENTER key:
//*-------------------------------------------------------------------
//| Title: //| SubClassProc //| //| Parameters: //| hWnd - Handle to the message's destination window //| wMessage - Message number of the current message //| wParam - Additional info associated with the message //| lParam - Additional info associated with the message //| //| Purpose: //| This is the window procedure used to subclass the edit control. //*--------------------------------------------------------------------- long FAR PASCAL SubProc(HWND hWnd, WORD wMessage,WORD wParam,LONG lParam) { switch (wMessage) { case WM_GETDLGCODE: return (DLGC_WANTALLKEYS | CallWindowProc(lpOldProc, hWnd, wMessage, wParam, lParam)); case WM_CHAR: //Process this message to avoid message beeps. if ((wParam == VK_RETURN) || (wParam == VK_TAB)) return 0; else return (CallWindowProc(lpOldProc, hWnd, wMessage, wParam, lParam)); case WM_KEYDOWN: if ((wParam == VK_RETURN) || (wParam == VK_TAB)) { PostMessage (ghDlg, WM_NEXTDLGCTL, 0, 0L); return FALSE; } return (CallWindowProc(lpOldProc, hWnd, wMessage, wParam, lParam)); break ; default: break; } /* end switch */
Method 3
This method involves using App Studio and ClassWizard and creating a new dialog box member function.
This method will allow a user to press the ENTER key and have the focus advance to the next edit control. If the focus is currently on the last edit control in the dialog box, the focus will advance to the first edit control. First, use App Studio to change the ID of the OK button of the dialog box. The default behavior of App Studio is to give the OK button the ID IDOK. The OK button's ID should be changed to another value, such as IDC_OK. Also, change the properties of the OK button so that it is not a default pushbutton. Next, use ClassWizard to create a new dialog box member funciton. Name the new member function something like OnClickedOK. This function should be tied to the BN_CLICKED message from the IDC_OK control. Once this is done, write the body of the OnClickedOK function. You should put the code that you would normally put in the OnOK function into the new OnClickedOK function, including a class's OnOK function. Add the following prototype to the header file for the dialog box: protected:
virtual void OnOK();
Add an OnOK function to the dialog box and code is as demonstrated below:
void CMyDialog::OnOK()
{ CWnd* pwndCtrl = GetFocus(); CWnd* pwndCtrlNext = pwndCtrl; int ctrl_ID = pwndCtrl->GetDlgCtrlID(); switch (ctrl_ID) { case IDC_EDIT1: pwndCtrlNext = GetDlgItem(IDC_EDIT2); break; case IDC_EDIT2: pwndCtrlNext = GetDlgItem(IDC_EDIT3); break; case IDC_EDIT3: pwndCtrlNext = GetDlgItem(IDC_EDIT4); break; case IDC_EDIT4: pwndCtrlNext = GetDlgItem(IDC_EDIT1); break; case IDOK: CDialog::OnOK(); break; default: break; } pwndCtrlNext->SetFocus(); }