The application itself is just a modal dialog box displayed
by the CWinApp::InitInstance(). After displaying the dialog box, InitInstance()
simply quits the application.
The important part of the sample takes
place in the dialog box class implementation: There are two edit controls. The
first takes input of an integer between 1 and 20. The second takes a character
string as input with length less than or equal to 5. When you tab or
mouse-click from control to control within the displayed dialog box, the
contents of the control that is losing focus are validated.
The application's functionality centers around the CFocusDlg
class and its implementation of four message handlers (discussed below). Normal
data exchange (DDX) and validation (DDV) using the routines provided by MFC
take place in OnInitialUpdate(), when the dialog box is first displayed, and
when the user chooses the OK button to accept the input. This is default
behavior provided by ClassWizard when member variables are connected to dialog
box controls and can be examined in the dialog class DoDataExchange() function.
Special Case 1
The first special case, for which this sample is written,
involves validating control contents when switching focus from one control to
the next. This is done by handling the EN_KILLFOCUS notification sent by the
edit control that is losing focus. The idea here is to check the contents and,
if they are not valid, to display the message box, inform the user, and then
set the focus back to the control from which it came.
Unfortunately,
some difficulties arise when trying to set the focus (or display the message
boxes) within a Killfocus message handler. At this point, Windows is in an
indeterminate state as it is moving focus from one control to the other. This
is a bad place to do the validation and SetFocus() call.
The
solution here is to post a user-defined message to the dialog box (parent) and
do the validation and SetFocus() there, thus waiting for a safer time to do the
work. (See "CFocusDlg::OnEditLostFocus()" in the file FOCUSDLG.CPP and
"WM_EDITLOSTFOCUS user-defined message" in the file FOCUSDLG.H.)
Another thing you will notice about this function is that it uses TRY/CATCH to
do the validation. The provided DDX/DDV routines throw CUserExceptions when
failing to validate or load a control's data. You should catch these and do the
SetFocus() in the CATCH block.
Special Case 2
The second special case is when the user clicks the Cancel button
while there is still invalid data in one of the controls. In this situation,
the user (normally) does not want to know whether his or her input is correct
because he or she is quitting the application. You might consider prompting the
user to save and validate the input, but normally Cancel means what it says.
Focus is sent to the Cancel button before the IDCANCEL notification is sent to
the dialog box, causing EN_KILLFOCUS to be generated by the control.
Because we want to do control validation, we maintain a flag called
"m_bValidate". When the user clicks Cancel, the dialog box is notified of the
mouse-click by the child button through a WM_PARENTNOTIFY message. If we handle
this message, we can set the m_bValidate flag to FALSE, which prevents the
validation from taking place in the OnEditLostFocus() function. Notice the "if
(m_bValidate)" right at the beginning.
IMPORTANT NOTE:
To ensure that the dialog receives the WM_PARENTNOTIFY message we
have to explicitly remove the WS_EX_NOPARENTNOTIFY style from the
Cancel button. For example:
BOOL CFocusDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Remove this style so we get the WM_PARENTNOTIFY when the
// user clicks on the Cancel button
GetDlgItem(IDCANCEL)->ModifyStyleEx(WS_EX_NOPARENTNOTIFY,0);
// Rest of OnInitDialog
...
...
}
Special Case 3
The third special case is when the user inputs bad data and then
decides to bring another application to the foreground, possibly to do some
other work or check some other information before completing this dialog box'
form. The edit control generates a EN_KILLFOCUS when the application loses
activation but, again, you might not necessarily want to validate. So, handle
the WM_ACTIVATEAPP message and based on the bActive flag that is passed as a
parameter, set the "m_bValidate" flag to disable validation. Upon reactivation,
the same message gets handled and this time the bActivate flag is TRUE, causing
us to re-enable validation.
Special Case 4
The fourth special case is when the user ends the Windows session
either through the Program Manager or the ExitWindows() function. In this case,
handle the WM_QUERYENDSESSION message. If the dialog box controls pass
validation (coded like the OnOK() function), then return TRUE from this handler
to allow Windows to terminate. Otherwise, return FALSE so that the user can
input the correct data.
For a combobox that is not a drop list combobox (i.e.
if there is an edit control associated with the combobox) the code in the
sample can cause infinite recursion.
The following code (in
focusdlg.cpp) is used in the sample to set the focus to the control if the
validation fails.
This should be modified so that the focus is set to the edit control of
the combobox if the validation fails.