Using Control Members

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:

m_winning_value_ctl.SetFocus();

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:

void CSetWinner::OnOK()

{

CString edit_string;

m_winning_value_ctl.GetWindowText(edit_string);

int value;

// Convert to an int

sscanf((const char *) edit_string, "%d", &value);

if (value < 1 || value > 40)

{

MessageBox("Winner outside of limits");

m_winning_value_ctl.SetWindowText("1");

m_winning_value_ctl.SetFocus();

}

else

CDialog::OnOK();

}

If you have complicated inter-field dependencies, you'll probably want

to set up some sort of validation like the above in the OnOK()

function.

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)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CSetWinner)

DDX_Control(pDX, IDC_WINNING_VALUE, m_winning_value_ctl);

DDX_Text(pDX, IDC_WINNING_VALUE, CSetWinner::);

//}}AFX_DATA_MAP

}

If you set up simple validation with ClassWizard, you won't need the

validation in the OnOK() function. It can look like:

void CSetWinner::OnOK()

{

CDialog::OnOK();

}

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

like:

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:

void CSetWinner::OnOK()

{

UpdateData(TRUE);

if (m_play_value < 1 || m_play_value > 40)

{

AfxMessageBox("Value out of range");

}

else

CDialog::OnOK();

}

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

differences.

In the next column, we'll continue our look at the control classes.

Insider: How the basic tech behind the Internet works
Join the discussion
Be the first to comment on this article. Our Commenting Policies