In the previous column, we introduced the control classes. We'll
continue our exploration of these classes this week. Last time, we
created a handler for the WM_INITDIALOG message called OnInitDialog().
Inside of that function, we enabled or disabled the edit control.
You can use any of the CWnd functions on a dialog member that
represents a control. You'll notice in the documentation that there
are many functions for CWnd. They include functions dealing with the
appearance and state of the window, drawing the window, and message-
handling functions. We'll cover CWnd in more detail in a later column.
At any time, there is one window that receives the keystrokes. That
window has "keyboard focus". As a user, you typically change the focus
by using the tab key. You can programmatically change this by using
the SetFocus() function. The function prototype is:
CWnd* CWnd::SetFocus( );
The function returns the window that previously had the focus. For
example, if you want to set the focus to the edit box in the SetWinner
dialog, you can code:
You can change the text in a control by using the SetWindowText()
function. Its prototype is:
BOOL SetWindowText( LPCTSTR p_string );
It returns FALSE if the function fails to set the text. Corresponding
is the GetWindowText() function which looks like:
int GetWindowText( LPTSTR p_string_buffer int max_count) const;
void GetWindowText(CString & string);
Let's set up a test to see if the value in the edit box control are
within a certain range. When the OK button is pushed, a message is
generated for which we'll set up a handler. For the SetWinner dialog
and the IDOK id, set up a handler for the BN_CLICKED message. The
default name is OnOK(). Inside that function, we'll get the value from
the text box. If it is out of range, we'll put up an error message,
set it to a legal value, and set the focus back to the edit box. If we
don't call the default handler (CDialog::OnOK()) for this message, then
the dialog does not end. The code looks like this:
// Convert to an int
sscanf((const char *) edit_string, "%d", &value);
if (value < 1 || value > 40)
MessageBox("Winner outside of limits");
If you have complicated inter-field dependencies, you'll probably want
to set up some sort of validation like the above in the OnOK()
If all you need to do is change the value in a control, rather than use
a control functions, you simply use a value member, as we've done
before. Note that the value member can be a string or a primitive such
as a int or a double. There are a couple of differences between
setting the control text directly and using a value member that
corresponds to the control.
First, using ClassWizard, you can set up some simple validation for a
value member (such as a low and high value or the length of a string).
Second, the values of the members and the values inside the controls do
not automatically correspond with each other. If you change the value
of a variable that represent a control, then you'll need to notify the
dialog that you have made those changes.
The UpdateData() function is used to move the member variable values
into the controls. You call UpdateData(FALSE) to set values in the
controls from class members. You call UpdateData(TRUE) to set the class
members from the values in the controls. The use of a boolean, rather
than having two separate functions (e.g. UpdateDataFromControls() and
UpdateControlsFromData()) is sometimes confusing, but unfortunately
that's the interface MFC uses.
The DoDataExchange function is what is called by UpdateData(). You
should never call this yourself. For example, if you use ClassWizard to
set up a variable of type int for the edit box, it will look like:
void CSetWinner::DoDataExchange(CDataExchange* pDX)
DDX_Control(pDX, IDC_WINNING_VALUE, m_winning_value_ctl);
DDX_Text(pDX, IDC_WINNING_VALUE, CSetWinner::);
If you set up simple validation with ClassWizard, you won't need the
validation in the OnOK() function. It can look like:
For example, if you set up a min and max value for m_winning_value,
then the AFX_DATA_MAP in DoDataExchange() will have a line that looks
DDV_MinMaxInt(pDX, m_winning_value, 1, 40);
This validation is performed each time UpdateData() is called. If the
value is out of range, an appropriate message is displayed.
If you don't set up a min and max in ClassWizard, you can still do
validation in OnOK(). You will need to get the values from the
controls with UpdateData(). For example:
if (m_play_value < 1 || m_play_value > 40)
AfxMessageBox("Value out of range");
If you set up both this validation and the one with ClassWizard, you'll
get two error messages for the same error.
Try using a control member for the edit box in the SetWinner dialog and
a value member for the edit box in the PlayGame dialog and examine the
In the next column, we'll continue our look at the control classes.