Tải bản đầy đủ (.pdf) (127 trang)

Ivor Horton’s Beginning Visual C++ 2005 phần 10 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.6 MB, 127 trang )

You have more than one control in the dialog box that you could use to permit a limit value to be
entered. You can use a
ListBox control to enable the user to select from a list of possible values, so you
can try that here. You should add two
Label controls to the dialog box form with two ListBox controls
alongside, as shown in Figure 21-21.
Figure 21-21
The
(Name) property for the list boxes should be lottoLowerList and lottoUpperList for the top
and bottom. As you see, I resized the
ListBox controls to be the same height as the Label controls and
a width sufficient to display a single limit value. I also changed the font
Size property to 10 and the
ScrollAlwaysVisible property to True. Make sure the SelectionMode property value is One for
both list boxes, as you want to allow only one item to be selected from a list box at one time.
The GUI for the dialog box is complete, but to make it do what you want you are back in coding mode
again. You can start with the code that populates the
ListBox controls with limit values.
Adding a List to a ListBox
The list that a ListBox controls is a set of objects that are stored as handles of type Object^, so any
kind of object can be stored in the list. In the example you want to store a set of integer limit values in
each list box, and for the most part you are able to rely on autoboxing and unboxing to convert values
of type
int to and from objects of type Int32 whenever necessary. The Items property for a ListBox
object returns a reference to a collection of the objects in the list box; this collection has an Add() method
that adds an object that you pass as the argument to the list. A
ListBox object has a large number of
properties including the
Enabled property that has the value true when the user can interact with the
list box and the value
false when interaction is to be inhibited.


The basic process for loading up the list for a list box is the same for both
ListBox controls, so you
could code a private function member of the
LottoLimitsDialog class that is generalized to add a
range of integers to a list box:
1058
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1058
void SetList(ListBox^ listBox, int min, int max, int selected)
{
listBox->BeginUpdate(); // Suppress drawing the listbox
for(int n = min ; n <= max ; n++)
listBox->Items->Add(n);
listBox->EndUpdate(); // Resume drawing the list box
listBox->SelectedItem = Int32(selected);
}
The arguments to the SetList() function are the list box for which the list is to be added, the minimum
and maximum integers in the range to be added, and the integer that is to be selected in the list box. The
function adds integers from
min to max inclusive to the list box using the Add() function for the collec-
tion object that is returned by the
Items property for the ListBox object. It also sets the selected value
as the item that is initially selected in the list when the list box is displayed by setting it as the value for
the
SelectedItem property for the list box.
When the user selects a limit in the dialog box, you’ll need somewhere to put the value so that it can
be accessed from a function belonging to the
Form1 object; the event handler for the menu items have
responsibility for retrieving the limit value and storing it in the
Form1 object. One way to do this is to

add a couple of private members to the
LottoLimitsDialog class to store the upper and lower limit
values and then add public properties to the class to make the values available externally. Adding the
following code to the
LottoLimitsDialog class definition does that:
private:
int lowerLimit; // Lower limit from control
int upperLimit; // upper limit from control
public:
property int LowerLimit // Property accessing lower limit
{
int get(){ return lowerLimit; }
void set(int limit)
{
lowerLimit = limit;
lottoLowerList->SelectedItem = Int32(limit);
}
}
property int UpperLimit // Property accessing upper limit
{
int get(){ return upperLimit; }
void set(int limit)
{
upperLimit = limit;
lottoUpperList->SelectedItem = Int32(limit);
}
}
1059
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1059

You need to be able to update the properties because the Click event handler for the Limits > Reset
menu item changes the limits, and you want the ListBox objects to have whatever is the current upper
or lower limit selected. As well as storing the value in the class object, you also update the
ListBox
objects to reflect the new limits.
You can now create two public member functions in the
LottoLimitsDialog class that sets up the two
ListBox controls:
public:
void SetLowerLimitsList(int min, int max, int selected)
{
SetList(lottoLowerList, min, max, selected);
lowerLimit = selected;
}
void SetUpperLimitsList(int min, int max, int selected)
{
SetList(lottoUpperList, min, max, selected);
upperLimit = selected;
}
Each function uses the SetList() function to set the range of values in the corresponding ListBox
object and then sets the selected value in the member for storing the limit.
Handling the Dialog Button Events
Add an event handler function for the Click event for the OK Button object, so return to the Design tab
for the
LottoLimitsDialog form and double-click the OK button to add the skeleton code.
You don’t need to add a handler for the
Click event for the Cancel button. The effect of clicking the
button is to close the dialog box and no further action is required.
You can implement the handler for the
Click event for the OK button like this:

System::Void lottoOK_Click(System::Object^ sender, System::EventArgs^ e)
{
// If there’s a currently selected upper limit item, save it
if(lottoUpperList->SelectedItem != nullptr)
upperLimit = safe_cast<Int32>(lottoUpperList->SelectedItem);
// If there’s a currently selected lower limit item, save it
if(lottoLowerList->SelectedItem != nullptr)
lowerLimit = safe_cast<Int32>(lottoLowerList->SelectedItem);
}
The function first stores the upper limit value from the lottoUpperList ListBox object in the member
variable you added for that purpose. The
SelectedItem property for a ListBox object makes the cur-
rently selected item available as a handle of type
Object^ and as a precaution the code verifies that the
handle returned is not null. Before storing the selected item you must cast it to its actual type—type
Int32. Auto-unboxing then takes care of converting the object to an integer. The handler next stores the
lower limit value from the other
ListBox object in the same way. When the handler finishes executing,
the dialog box is closed automatically.
1060
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1060
Controlling the State of the ListBox Objects
The same dialog object is used in the response to the Click events for both the Limits > Upper and
Limits > Lower menu items, but you don’t want to allow both list boxes to be changed in either case.
For the
Upper menu item event you’ll want the selection of a lower limit to be inhibited, and for the
Lower menu item you’ll want the list box for the upper limit to be inhibited. You could add a couple of
public function members to the
LottoLimitsDialog class to make this possible. Here’s the function to

set the state of the
ListBox objects for the Upper menu item:
void SetUpperEnabled()
{
lottoUpperList->Enabled = true; // Enable upper list box
lottoLowerList->Enabled = false; // Disable lower list box
}
You set the Enabled property for the lottoUpperList object to true to allow the user to interact with
it. Setting the
Enabled property for lottoLowerList to false makes it read-only.
For the
Lower menu item you do the reverse:
void SetLowerEnabled()
{
lottoUpperList->Enabled = false; // Disable upper list box
lottoLowerList->Enabled = true; // Enable lower list box
}
You have done a lot of work to get the dialog object to behave as you want in the application, but you
don’t yet have a dialog object. The application window object takes care of that.
Creating the Dialog Object
The Form1 class constructor can create the dialog object. It can also initialize the ListBox objects in the
dialog. Add a private member to the Form1 class that stores the handle to the dialog box:
private: LottoLimitsDialog^ lottoLimitsDialog;
Add the following lines of code to the body of the Form1 constructor:
lottoLimitsDialog = gcnew LottoLimitsDialog;
lottoLimitsDialog->SetLowerLimitsList(1, lottoUpperLimit-lottoValuesCount+1,
lottoUserMinimum);
lottoLimitsDialog->SetUpperLimitsList(lottoValuesCount, lottoUpperLimit,
lottoUserMaximum);
This code is very straightforward. The first statement creates the dialog object. The next two statements

call the functions that initialize the lists in the
ListBox objects. The maximum value in the ListBox
object that sets the lower limit is calculated so that it permits the required number of values for an entry
to be created. If the maximum value for a value is 49 and the number of values in an entry is 6, the maxi-
mum for the lower limit must be 44 — if it was any higher you could not create six different values. The
same reason applies to the minimum value for the upper limit; it cannot be less than the number of val-
ues in an entry. The selected item for the list boxes are the
lottoUserMinimum and lottoUserMaximum
values.
1061
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1061
Because you refer to the LottoLimitsDialog class name in the Form1 class constructor, you’ll need to
add an
#include directive for the class definition to Form1.h:
#include “LottoLimitsDialog.h”
Using the Dialog Box
You’ll put the dialog box into operation in the code for the Click event handlers for the Upper and
Lower menu items in the Limits menu. To display a dialog box as a modal dialog box, you call the
ShowDialog() function for the dialog object. Optionally you can pass the handle to the parent form
as the argument to the
ShowDialog() function. You can implement the Click event handler functions
like this:
System::Void lowerMenuItem_Click(System::Object^ sender, System::EventArgs^ e)
{
if(lottoTab->Visible)
{
lottoLimitsDialog->SetLowerEnabled();
::DialogResult result = lottoLimitsDialog->ShowDialog(this);
if(result == ::DialogResult::OK)

{
// Update user limits from dialog properties
lottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}
}
}
System::Void upperMenuItem_Click(System::Object^ sender, System::EventArgs^ e)
{
if(lottoTab->Visible)
{
lottoLimitsDialog->SetUpperEnabled();
::DialogResult result = lottoLimitsDialog->ShowDialog(this);
if(result == ::DialogResult::OK)
{
// Update user limits from dialog properties
lottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}
}
}
These two functions both work in the same way; they call the function to set the list box states and then
display the dialog box as a modal dialog box by calling the
ShowDialog() function for the dialog object.
If you wanted to display the dialog box as a modeless dialog box, you call the
Show() function for the
dialog object instead.
When you call the
ShowDialog() function, it does not return until the dialog box closes. This means
that the code to update the limits is not executed until the new limits have been recorded in the dialog

1062
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1062
object by the Click event handler for the lottoOK button. When you display a dialog box as modeless
by calling the
Show() function, the function returns immediately. Thus if you need to be able to access
data that might have been changed in the dialog box, you need another way to do it. Adding a handler
function for the
Closing event for the dialog form is one possibility; another would be to deal with
transferring the data in the handler for the button that closes the dialog box.
The
ShowDialog() function returns a value of the enumeration type DialogResult and you store this in
the local variable,
result. The return value from the ShowDialog() function indicates which button in
the dialog was clicked, and if the value is the enumeration constant
::DialogResult::OK, it indicates that
the
OK button was clicked. Thus the code in each handler function updates only the lottoUserMaximum
and LottoUserMinimum fields when the OK button was used to close the dialog.
Note the use of the
:: operator in the type specification ::DialogReult and in the expression
::DialogResult::OK. The scope resolution operator is necessary preceding the DialogResult name
to distinguish the name of the enumeration at global scope from the property with the same name that
is a member of the
Form1 class.
Of course, you could access the
DialogResult property for the lottoLimitsDialog object directly, so
you could write the
if statement as:
if(lottoLimitsDialog->DialogResult == ::DialogResult::OK)

{
// Update user limits from dialog properties
lottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}
The former version is better because it is obvious that you are checking the value returned by the
ShowDialog() function.
The complete set of constants that the
DialogResult enumeration defines is as follows:
Yes No OK Cancel
Retry Ignore None
The DialogResult property value that is set for a dialog box determines whether or not the dialog box
closes. If the value is set to
None, the dialog box does not close. You can set the property to this value when
you want to prevent the dialog box from closing for some reason—if the input is not valid, for instance.
At the moment, the
Click event handler for the OK button does not validate the input values. It is cur-
rently quite possible to set the lower and upper limits to values that make it impossible to assign six
unique values for the lottery entry. You can use the
DialogResult property for the form to deal with
the problem.
Validating the Input
The difference between the upper and lower limits that the user chooses must be greater than or equal to
5 if there are to be 6 unique values in a Lotto entry. You could modify the
Click event handler for the OK
button in the dialog class to check for this:
1063
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1063
System::Void lottoOK_Click(System::Object^ sender, System::EventArgs^ e)

{
int upper = 0;
int lower = 0;
// If there’s a currently selected upper limit item, save it
if(lottoUpperList->SelectedItem != nullptr)
upper = safe_cast<Int32>(lottoUpperList->SelectedItem);
// If there’s a currently selected lower limit item, save it
if(lottoLowerList->SelectedItem != nullptr)
lower = safe_cast<Int32>(lottoLowerList->SelectedItem);
if(upper - lower < 5)
{
MessageBox::Show(L”Upper limit: “ + upper + L” Lower limit: “ + lower +
L”\nUpper limit must be at least 5 greater that the lower limit.” +
L”\nTry Again.”,
L”Limits Invalid”,
MessageBoxButtons::OK,
MessageBoxIcon::Error);
DialogResult = ::DialogResult::None;
}
else
{
upperLimit = upper;
lowerLimit = lower;
}
}
Now the function saves the values selected in the ListBox objects in the local variables lower and
upper. If the values differ by less than 5, a message box is displayed and closing of the dialog box is
inhibited by setting the value of the
DialogResult property to None. The static Show() function in the
MessageBox class displays a message box that is customized by the arguments to the function. This ver-

sion of the
Show() function used here accepts four arguments as follows:
Parameter Type Description
String^ The text to be displayed in the message box.
String^ The text to appear in the title bar of the message box.
MessageBoxButtons An enumeration constant specifying the buttons to appear in the
message box. The
MessageBoxButtons enumeration defines the
following values:
OK, OKCancel, YesNo, YesNoCancel, RetryCancel,
AbortRetryIgnore
MessageBoxIcon An enumeration constant specifying the icon to appear in the message
box. The
MessageBoxIcon enumeration defines the following values:
Asterisk, Exclamation, Error, Hand, Information, None,
Question, Stop, Warning
1064
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1064
There are a significant number of overloaded versions of the static Show() function that range from the
very simple with a single parameter of type
String^ to the rather more complicated with up to 10
parameters.
If you compile and execute the example and set the limits inappropriately, you’ll see a window similar to
that shown in Figure 21-22.
Figure 21-22
The circular red icon with the white cross was specified by the fourth argument to the
Show() function,
and the single
OK button is the result of the third argument.

The
Show() function that you call to display a message box returns a value of type DialogResult that
indicates which button was used to close the message box. You can use this return value to decide what
to do after the message box closes. In the
lottoOK_Click() handler for the OK button in the limits dia-
log box, you could decide whether or not to close the limits dialog box using the value returned by the
Show() function for the message box:
System::Void lottoOK_Click(System::Object^ sender, System::EventArgs^ e)
{
int upper = 0;
int lower = 0;
// If there’s a currently selected upper limit item, save it
if(lottoUpperList->SelectedItem != nullptr)
upper = safe_cast<Int32>(lottoUpperList->SelectedItem);
// If there’s a currently selected lower limit item, save it
if(lottoLowerList->SelectedItem != nullptr)
lower = safe_cast<Int32>(lottoLowerList->SelectedItem);
if(upper - lower < 5)
{
1065
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1065
::DialogResult result =
MessageBox::Show(L”Upper limit: “ + upper + L” Lower limit: “ + lower +
L”\nUpper limit must be at least 5 greater that the lower limit.” +
L”\nTry Again.”,
L”Limits Invalid”,
MessageBoxButtons::OKCancel,
MessageBoxIcon::Error);
if(result == ::DialogResult::OK)

DialogResult = ::DialogResult::None;
else
DialogResult = ::DialogResult::Cancel;
}
else
{
upperLimit = upper;
lowerLimit = lower;
}
}
Because the third argument to the Show() function is MessageBoxButtons::OKCancel, the message
box now has two buttons as shown in Figure 21-23.
Figure 21-23
In the
Click event handler for the OK button in the limits dialog box you store the return value from the
Show() function in result. The type for result has to be specified using the scope resolution operator.
Otherwise, it is interpreted by the compiler as the
DialogResult property for the lottoLimitsDialog
object, and the code does not compile. If result contains the value ::DialogResult::OK, you set the
DialogResult property for the lottoLimitsDialog object to ::DialogResult::None, which prevents
the dialog box from closing and allows the limit to be changed. Otherwise you set the
DialogResult
property for the dialog to ::Dialog::Cancel, which has the same effect as clicking the Cancel button
for the dialog box so it closes.
1066
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1066
Handler the Reset Menu Item Event
You can implement the event handler for the Reset menu item like this:
System::Void resetMenuItem_Click(System::Object^ sender, System::EventArgs^ e)

{
if(lottoTab->Visible)
{
// Reset user limits for Lotto
lottoUserMaximum = lottoUpperLimit;
lottoUserMinimum = lottoLowerLimit;
lottoLimitsDialog->UpperLimit = lottoUpperLimit;
lottoLimitsDialog->LowerLimit = lottoLowerLimit;
}
else if(euroTab->Visible)
{
// Reset user limits for Euromillions
euroUserMaximum = euroUpperLimit;
euroUserMinimum = euroLowerLimit;
euroStarsUserMaximum = euroStarsUpperLimit;
euroStarsUserMinimum = euroStarsLowerLimit;
// Code to update Euromillions limits dialog
}
}
This just resets the limits in the fields in the Form1 object and then updates the properties in the dialog
object accordingly. You still have to add code to this function to deal with resetting the dialog box to
which you have yet added the application that will handle the input for the Euromillions lottery limits.
You can now recompile the program and try changing the limits for the Lotto entry. A typical application
window is shown in Figure 21-24.
Figure 21-24
1067
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1067
As you see, you automatically get a scrollbar for scrolling through the list of items in a list box. Note that
scrolling to a given item does not select it. You must also click on the item to select it before clicking the

OK button. Selecting the Limits > Reset menu item resets both limits to their original values.
Adding the Second Dialog
The second dialog box for setting limits for the Euromillions lottery is going to be easy; it’s the same
process as for the first dialog box. Create a new form in the project by pressing
Ctrl+Shift+A to display
the
Add New Item dialog box and select the UI category and the Windows Form template; the name
should be
EuroLimitsDialog. You can set property values for this dialog box in much the same way
as for the previous dialog.
Form Property Value to be Set
FormBorderStyle FixedDialog
ControlBox False
MinimizeBox False
MaximizeBox False
Text Set Euromillions Limits
You can add
OK and Cancel buttons to the dialog form next. Set the Text property values for the buttons
to
“OK” and “Cancel” and the (Name) property values to euroOK and euroCancel, respectively. You
should also set the
DialogResult property values to OK and Cancel. With the buttons defined, you can
return to the properties for the dialog form and set the
AcceptButton and CancelButton property val-
ues to
euroOK and euroCancel, respectively.
In the interests of getting experience of a wider range of controls, you’ll forego consistency in the appli-
cation, and you won’t use
ListBox controls to handle the input as you did in the first dialog box. In this
dialog box you need to provide for the entry of upper and lower limits for the set of five values as well

as the set of two stars. It won’t make for a very elegant implementation, but to maximize the variety of
controls you work with you’ll use
NumericUpDown controls for the former and ComboBox controls for
the latter. You can add these controls together with associated
Label controls to the dialog form with
each group of controls placed within a
GroupBox control, as illustrated in Figure 21-25. Obviously
“you’ll need to add the
GroupBox controls first and then place the other controls within them.
To identify the function of the controls within each group box, the value for the
Text property for the
upper group box has been set to
“Set Values Limits” and that of the lower group box “Set Stars
Limits”
. You won’t be accessing the GroupBox objects in the code so the (Name) property values for
these are of no importance. You can set the value of the
Text property for each Label control as shown
in Figure 21-25.
The values for the
(Name) properties for the NumericUpDown controls in the upper group box should be
set to
lowerValuesLimits and upperValuesLimits. You can set the values that these controls display
by setting values for the
Maximum and Minimum properties. These values for the lowerValuesLimits
control on the left should be 44 and 1 respectively, and the values for the Maximum and Minimum prop-
erties for the control to the right should be 49 and 6 respectively. You can set the value of the
Value
1068
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1068

property for the upperValuesLimit control to 49; this is the value displayed initially in the control. If
you also set the
ReadOnly property value for each of the NumericUpDown controls to True, this prevents
the entry of a value from the keyboard. You are using the
NumericUpDown control very simply here. You
can change the up down increment by setting the
Increment property value. The Increment property
is of type
Decimal so you can set this to non-integral values, too.
Figure 21-25
You can set the values of the
(Name) property for the ComboBox controls in the lower group box to
lowerStarsLimits and upperStarsLimits. You can enter values to be displayed in a ComboBox quite
easily. Click the small arrow at the top right of the leftmost
ComboBox control to display the menu shown
in Figure 21-26.
Figure 21-26
1069
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1069
Select the Edit Items menu item at the bottom of the menu to display the dialog window for the
String Collection Editor shown in Figure 21-27.
Figure 21-27
Figure 21-27 shows the values entered for the
ComboBox control on the left. For the ComboBox control on
the right you can enter the values from 2 to 9 inclusive.
The
ComboBox is not ideal for this application because it allows text input as well as selection from a list;
you want to have a limit value selected only from the list. The control gets its name,
ComboBox, because

it combines the function of a
ListBox control that allows selection from a list with that of a TextBox
control that provides for text input.
Getting the Data from the Dialog Controls
You’ll get the limit values back from the controls in essentially the same way as you did for the dialog
box for Lotto limits. You can add some new data members to the
EuroLimitsDialog class to hold the
user limit values first:
private:
int lowerValuesLimit;
int upperValuesLimit;
int lowerStarsLimit;
int upperStarsLimit;
To be on the safe side you had better initialize these members in the class constructor:
EuroLimitsDialog(void)
:lowerValuesLimit(1)
,upperValuesLimit(50)
,lowerStarsLimit(1)
,upperStarsLimit(9)
{
1070
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1070
InitializeComponent();
//
//TODO: Add the constructor code here
//
}
You’ll also need some public properties defined in the dialog class to make the limits accessible from the
application window object:

public:
property int LowerValuesLimit
{
int get() { return lowerValuesLimit; }
void set(int limit)
{
lowerValuesLimit = limit;
lowerValuesLimits->Value = limit; // Set as selected in NumericUpDown
}
}
property int UpperValuesLimit
{
int get() { return upperValuesLimit; }
void set(int limit)
{
upperValuesLimit = limit;
upperValuesLimits->Value = limit; // Set as selected in NumericUpDown
}
}
property int LowerStarsLimit
{
int get() { return lowerStarsLimit; }
void set(int limit)
{
lowerStarsLimit = limit;
lowerStarsLimits->SelectedItem = limit; // Set as selected in ComboBox
lowerStarsLimits->SelectedIndex = // Set index for selected item
lowerStarsLimits->FindString(limit.ToString());
}
}

property int UpperStarsLimit
{
int get() { return upperStarsLimit; }
void set(int limit)
{
upperStarsLimit = limit;
upperStarsLimits->SelectedItem = limit; // Set as selected in ComboBox
upperStarsLimits->SelectedIndex = // Set index for selected item
upperStarsLimits->FindString(limit.ToString());
}
}
1071
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1071
The get() function for each property returns the value of the corresponding private member of the dialog
class. The
set() function sets the value of the data member and also updates the control in the dialog box
so that the value set becomes the selected value. The
SelectedIndex property value is the index to the
selected item. You set this using the
FindString() function for the ComboBox control that returns the
index value for the first occurrence of the argument in the control’s collection of items. The value at this
position is displayed initially in the control.
Add a
Click event handler for the OK button in the EuroLimitsDialog class by double-clicking the
button in the Design window. You won’t need to implement a handler for the
Cancel button. You can
implement the
OK button handler like this:
System::Void euroOK_Click(System::Object^ sender, System::EventArgs^ e)

{
::DialogResult result;
// get the limits for values
int valuesLower = Decimal::ToInt32(lowerValuesLimits->Value);
int valuesUpper = Decimal::ToInt32(upperValuesLimits->Value);
if(valuesUpper - valuesLower < 4) // Check for an adequate range
{
result = MessageBox::Show(this, // Range insufficient so
“Upper values limit: “+valuesUpper + // display message box
“ Lower values limit: “+ valuesLower+
“\nUpper values limit must be at least 4 greater that the lower limit.”+
“\nTry Again.”,
“Limits Invalid”,
MessageBoxButtons::OKCancel,
MessageBoxIcon::Error);
if(result == ::DialogResult::OK) // If message box OK clicked
DialogResult = ::DialogResult::None; // prevent dialog from closing
else // Messag box Cancel clicked
DialogResult = ::DialogResult::Cancel; // so close the dialog
return;
}
// Get stars limits
int starsLower = lowerStarsLimits->SelectedItem == nullptr ?
lowerStarsLimit :
Int32::Parse(lowerStarsLimits->SelectedItem->ToString());
int starsUpper = upperStarsLimits->SelectedItem == nullptr ?
upperStarsLimit :
Int32::Parse(upperStarsLimits->SelectedItem->ToString());
if(starsUpper - starsLower < 1) // Check for an adequate range
{

result = MessageBox::Show(this, // Range insufficient so
“Upper stars limit: “+starsUpper + // so display message box
“ Lower stars limit: “+ starsLower+
“\nUpper stars limit must be at least 1 greater that the lower limit.”+
“\nTry Again.”,
“Limits Invalid”,
1072
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1072
MessageBoxButtons::OKCancel,
MessageBoxIcon::Error);
if(result == ::DialogResult::OK) // If message box OK clicked
DialogResult = ::DialogResult::None; // prevent dialog from closing
else // Message box Cancel clicked
DialogResult = ::DialogResult::Cancel; // so close the dialog
}
// Store the new limits
lowerValuesLimit = valuesLower;
upperValuesLimit = valuesUpper;
lowerStarsLimit = starsLower;
upperStarsLimit = starsUpper;
}
The Value property for a NumericUpDown control returns a value of type Decimal. To convert this to
type
Int32 you pass it as the argument to the static ToInt32() function in the Decimal class. The value
that this function returns is automatically unboxed so that it can be stored in the variable of type
int.
The value returned by the
SelectedItem property for a ComboBox control is of type Object^, so to be
on the safe side you check whether it is null. If it is null, you set the local variable to the current value

recorded in the dialog object; if it isn’t null, you store the value represented by the
SelectedItem prop-
erty. You can’t store the value directly, but calling the
ToString() function for the object produces a
string representation of the object that you are then able to convert to type
int using the static Parse()
function in the Int32 class.
You will need a private member of the
Form1 class that stores a handle to the new dialog box:
private:
EuroLimitsDialog^ euroLimitsDialog; // Dialog to set Euromillions limits
You can add the following statements to the end of the code in the Form1 class constructor to create the
dialog object and update the properties for the stars limit values:
euroLimitsDialog = gcnew EuroLimitsDialog;
euroLimitsDialog->LowerStarsLimit = euroStarsLowerLimit;
euroLimitsDialog->UpperStarsLimit = euroStarsUpperLimit;
By setting the LowerStarsLimit and UpperStarsLimit properties for the dialog object, you ensure
that the
ComboBox controls show these values when the dialog box is initially displayed. If there is no
selected item set for a
ComboBox control, it displays nothing initially.
Don’t forget to add the
#include directive for the EuroLimitsDialog class definition to Form1.h:
#include “EuroLimitsDialog.h”
Disabling Input Controls
When the Limits > Upper menu item is clicked, you want to prevent the input for a lower limit being
entered, and when the
Limits > Lower menu item is selected, you want to prevent input for an upper
limit value. You can add a couple of member functions to the
EuroLimitsDialog class to make this

possible:
1073
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1073
public:
// Disables controls for selecting upper limits
void SetLowerEnabled(void)
{
upperValuesLimits->Enabled = false;
upperStarsLimits->Enabled = false;
lowerValuesLimits->Enabled = true;
lowerStarsLimits->Enabled = true;
}
// Disables controls for selecting lower limits
void SetUpperEnabled(void)
{
upperValuesLimits->Enabled = true;
upperStarsLimits->Enabled = true;
lowerValuesLimits->Enabled = false;
lowerStarsLimits->Enabled = false;
}
The value of the Enabled property for a control determines whether it is enabled. A true value enables the
control, and a value of
false disables it so the user cannot interact with it. The SetLowerEnabled() func-
tion disables the controls used to enter upper limits and enables those for entry of lower limits. The
SetUpperEnabled() function does the reverse.
Updating the Limits Menu Item Handlers
The last step to complete the support for entering limits for the Euromillions lottery is to update the
Click event handlers in the Form1 class for the items in the Limits menu. The handler for the Upper
menu item should be modified as follows:

System::Void upperMenuItem_Click(System::Object^ sender, System::EventArgs^ e)
{
::DialogResult result;
if(lottoTab->Visible)
{
lottoLimitsDialog->SetUpperEnabled();
result = lottoLimitsDialog->ShowDialog(this);
if(result == ::DialogResult::OK)
{
lottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}
}
else if(euroTab->Visible)
{
euroLimitsDialog->SetUpperEnabled();
result = euroLimitsDialog->ShowDialog(this);
if(result == ::DialogResult::OK)
{
euroUserMaximum = euroLimitsDialog->UpperValuesLimit;
euroUserMinimum = euroLimitsDialog->LowerValuesLimit;
euroStarsUserMaximum = euroLimitsDialog->UpperStarsLimit;
1074
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1074
euroStarsUserMinimum = euroLimitsDialog->LowerStarsLimit;
}
}
}
The local variable result is used in both if statements, so it is now declared at the beginning of the

function. After enabling the controls in the dialog box appropriately by calling the
SetUpperEnabled()
function for the dialog object, you display the dialog box as modal. If the user closes the dialog box by
clicking the
OK button, you store the results available through the properties of the dialog object.
The changes to the handler for the
Click event for the Lower menu item are very similar:
System::Void lowerMenuItem_Click(System::Object^ sender, System::EventArgs^ e)
{
::DialogResult result;
if(lottoTab->Visible)
{
lottoLimitsDialog->SetLowerEnabled();
result = lottoLimitsDialog->ShowDialog(this);
if(result == ::DialogResult::OK)
{
lottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}
}
else if(euroTab->Visible)
{
euroLimitsDialog->SetLowerEnabled();
result = euroLimitsDialog->ShowDialog(this);
if(result == ::DialogResult::OK)
{
euroUserMaximum = euroLimitsDialog->UpperValuesLimit;
euroUserMinimum = euroLimitsDialog->LowerValuesLimit;
euroStarsUserMaximum = euroLimitsDialog->UpperStarsLimit;
euroStarsUserMinimum = euroLimitsDialog->LowerStarsLimit;

}
}
}
The logic here is the same as in the previous handler function.
Implementing the Help | About Menu Item
This is easy now that you know about the MessageBox class. You can just show a message box when the
Help > About menu item is clicked:
System::Void aboutToolStripMenuItem_Click(System::Object^ sender,
System::EventArgs^ e)
{
MessageBox::Show(L”(c) Copyright Ivor Horton”, L”About A Winning Application”,
MessageBoxButtons::OK, MessageBoxIcon::Exclamation);
}
1075
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1075
When the the menu item is clicked, the handler function displays the message box shown in Figure 21-28.
Figure 21-28
Handling a Button Click
Clicking a button should change the value on the button to a new random value. Of course, the value
must be different from values on the other buttons as well as being different from the value for the but-
ton that was clicked. It would be a good idea to present the whole set in sorted order; this may result in
the new value being on a different button, but that’s likely to be better that not having the values in
sequence.
The process for handling a button click is going to be the same for all the buttons, so you’ll be able to
economize on code by creating a generalized function to do the work. You can define a private function
member of the
Form1 class that generates a new value for a given Button object from an array of buttons:
// Generates a new value for button different from current button values
void SetNewValue(Button^ button, array<Button^>^ buttons,

int lowerLimit, int upperLimit)
{
int index = 0; // Index of button in buttons
// Array to store button values
array<int>^ values = gcnew array<int>(buttons->Length);
// Get values from buttons and find index for button
for(int i = 0 ; i < values->Length ; i++)
{
values[i] = Int32::Parse(buttons[i]->Text); // Get current button value
// If current handle is same as button, save the index value
1076
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1076
if(button == buttons[i])
index = i;
}
int newValue = 0; // Store the new button value
// Check if it is different from the other button values
for(;;) // Loop until we get a good one
{
newValue = random->Next(lowerLimit, upperLimit); // Generate a value
if(IsValid(newValue, values, values->Length)) // If it’s OK
break; // end the loop
}
values[index] = newValue; // Store the new value at index
Array::Sort(values); // Sort the value
for(int i = 0 ; i < values->Length ; i++) // and set the values
buttons[i]->Text = values[i].ToString(); // as text on the buttons
}
The first two function parameters are the button that is to have a new number and the array of buttons

in the group to which the first button belongs. The next two parameters specify the lower and upper
limits for the values. The current values of the buttons in the array are stored in the values array in the
first loop. This loop also finds the index value in the
buttons array for the Button^ handle that is the
first argument. You need this so you know which element of the
values array is to be replaced by a
new value.
The new value is created in the indefinite
for loop. This is the same mechanism that you used to
create the values for the button in the first instance. After you have a valid new value, you store it in the
values array. You then sort the elements in the values array before storing them as the values for the
Text properties for the buttons in the buttons array. You’ll be able to use this function for dealing with
the
Click events for all of the buttons.
If you have not already done so, double-click the first button on the Lotto tab to generate a
Click event
handler function for it. You can edit the name of the handler function by opening the
Properties tab
for the button, selecting the
Events button, and changing the value for the Click event. When you
press Enter, the code is updated with the new name. I changed the value to
lottoValue_Click.
You can amend the
Click event handler to call the SetNewValue() function you have just added to the
Form1 class:
System::Void lottoValue_Click(System::Object^ sender, System::EventArgs^ e)
{
Button^ button = safe_cast<Button^>(sender);
// Create the array of button handles
array<Button^>^ buttons = {lottoValue1, lottoValue2, lottoValue3,

lottoValue4, lottoValue5, lottoValue6};
// Replace the value on button
SetNewValue(button, buttons, lottoUserMinimum, lottoUserMaximum);
}
1077
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1077
The availability of the SetNewValue() function makes this handler function very simple. The first state-
ment stores the handle to the button that was clicked. The first parameter to the event handler is a han-
dle to the object that originated the event, so all that’s necessary is to cast it to the appropriate type. You
then just assemble the handles for the buttons in an array and call the new function — job done!
You still have to deal with the
Click event for the other buttons on the Lotto tab, but this doesn’t require
any more code. Open the
Properties window for the second button and then click the Events button.
If you click the
Click event value, you’ll see a list of the existing event handlers as shown in Figure 21-29;
if you select
lottoValue_Click from the list, the event handler for the first button will be registered as
the event handler for the second button, too.
Figure 21-29
You can repeat the process for the remaining four buttons on the Lotto tab so that the one event handler
is called in response to the
Click event for any of the buttons on the Lotto tab.
The
Click event handlers for the buttons on the Euromillions tab are going to be very easy. Double-click
the first of the five buttons in the
Values group to create the event handler. Open the Properties window
for the button and change the value for the
Click event to euroValue_Click. You can then modify the

code for the handler like this:
1078
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1078
System::Void euroValue_Click(System::Object^ sender, System::EventArgs^ e)
{
Button^ button = safe_cast<Button^>(sender);
array<Button^>^ buttons = {euroValue1, euroValue2, euroValue3,
euroValue4, euroValue5 };
SetNewValue(button, buttons, euroUserMinimum, euroUserMaximum);
}
This works exactly the same as the handler for the Lotto buttons. The array contains the handles to the five
buttons in the values group, and the
SetNewValue() function does the rest. If you open the Properties
window for each of the remaining four buttons in the group, you can select this function to respond to the
Click event for each of them. Be sure you select euroValue_Click and not lottoValue_Click!
Follow the same procedure for the Stars buttons on the Euromillions tab. You can implement the
handler as:
System::Void euroStar_Click(System::Object^ sender, System::EventArgs^ e)
{
Button^ button = safe_cast<Button^>(sender);
array<Button^>^ buttons = { euroStar1, euroStar2 };
SetNewValue(button, buttons, euroStarsUserMinimum, euroStarsUserMaximum);
}
Set the handler for the Click event for the second button to be euro_StarClick and you are done. If
you recompile the example, you should be able to generate a new value for any button on either tab just
by clicking it. The last piece to complete the example is to allow the user to enter a value for a button.
Responding to the Context Menu
Right-clicking a button brings up a context menu with a single menu item, Choose. When the user clicks
this item, the program should display a dialog box that allowed a suitable value to be entered. Click the

name of the context menu in the Design tab for
Form1 and then double-click the menu item to create the
Click event handler.
The first problem is to determine which group of buttons was clicked to cause the event. Each group on
buttons is in its own
GroupBox control, and the GroupBox class has a Controls property that returns a
reference to an object of type
Control::ControlCollection that represents the collection of controls
in the group box. The
Control::ControlCollection class defines the Contains() function that
returns
true if the control that you pass as the argument is within the collection and false otherwise.
Thus you have a way to determine to which group of buttons the button causing the
Click event
belongs. An outline implementation of the event handler looks like this:
System::Void chooseValue_Click(System::Object^ sender, System::EventArgs^ e)
{
// Get the button that was clicked for the context menu, then
if(lottoValues->Controls->Contains(theButton))
{
// the button is from the lotto group
}
else if(euroValues->Controls->Contains(theButton))
{
1079
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1079
// The button is in the Values group
}
else if(euroStars->Controls->Contains(theButton))

{
// The button is in the Stars group
}
}
That sorts out which group of buttons is involved, at least in principle. But there’s still a bit of a problem—
how do you find out which button was right-clicked to open the context menu?
The
chooseValue_Click() handler is called when the Choose menu item is clicked, so the sender
parameter for the handler identifies the menu item, not the button. You need a handler that responds to
the original click on the button and you can create this by double-clicking
buttonContextMenu in the
Design pane for
Form1. You can complete the code for the handler function that is created like this:
System::Void buttonContextMenu_Opening(System::Object^ sender,
System::ComponentModel::CancelEventArgs^ e)
{
contextButton = safe_cast<Button^>(buttonContextMenu->SourceControl);
}
This casts the sender handle to type Button^ and stores it in the contextButton member of the Form1
class. Because in this case the event is for the context menu, the sender parameter identifies the compo-
nent that was clicked to display it. Of course, you have yet to add the
contextButton variable as a pri-
vate member of the
Form1 class:
private:
Button^ contextButton; // Button that was right-clicked for context menu
All you need to do now is figure out what to do next.
The Logic for Dealing with the Choose Menu Item
The process for responding to the Choose item being clicked can be the same whichever group of but-
tons is involved, and it could work something like the following:

1. Display a dialog box to allow a value to be entered.
2. Check that the value is valid — that is, within range and different from the other buttons.
3. Display a message box if the value is not valid and allow the entry to be retired or the dialog
operation to be cancelled.
4. If the value is valid, update the button that was right-clicked with the new value.
The first step in implementing this process is to create a new dialog form.
Creating the Dialog Form
Press Ctrl+Shift+A to display the Add New Item dialog box; then select UI as the category and
Windows Form as the template. Enter the name as UserValueDialog and click the Add button. You can
1080
Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1080
now open the Properties window for the form by pressing F4 and setting the property values to make
it a dialog box. Set the
ControlBox, MinimizeBox, and MaximizeBox property values to False, and set
the
Text property to “User Value Input.”
Add OK and Cancel buttons to the dialog form as well as a Label control and a TextBox control, as
shown in Figure 21-30.
Figure 21-30
Set the
Text and (Name) property values for the OK button to OK, and the value for the DialogResult
property should be set to OK. The values for the Text, (Name), and DialogResult properties for the
Cancel button should be
Cancel. Set the value of the (Name) property for the TextBox control to be
textBox and the value for the TextAlign property as Center. The (Name) property for the Label con-
trol can be
label, and the Text property can be anything you like because you’ll change this in the
code to suit the circumstances.
You can now display the properties for the dialog form once again and set the values for the

AcceptButton and CancelButton properties to OK and Cancel, respectively.
Developing the Dialog Class
The value entered in the TextBox control must be available to the Form1 object, so add a property to the
UserValueDialog class to store it:
public:
property int Value;
This is an example of a trivial scalar property so get() and set() functions are supplied by default.
The dialog object needs to know what the limits are for the value because the handler for the OK button
in the dialog class is verifying that the value is legal. For the same reason, the dialog object needs to
know what the current values on the buttons are to ensure they are not duplicated. You could add three
further property members to the
UserValueDialog class to store the data:
1081
Applications Using Windows Forms
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1081
public:
property int LowerLimit;
property int UpperLimit;
property array<int>^ Values; // Current button values
The Form1 object needs to be able to change the value of the Text property for the label control, depend-
ing on the limits in effect for button values when the dialog box is displayed; you can add a public member
function to the
UserValidDialog class to do this:
public:
void SetLabelText(int lower, int upper)
{
label->Text = L”Enter your value between “ + lower +L” and “ + upper;
}
You could conceivably pick up the limits from the properties in the dialog object, but this would require
that the properties were always set first. By using parameters for the limits you remove this dependency.

You can create the dialog object in the
Form1 class constructor, but you’ll need to add a private Form1
class member to store the handle:
private: UserValueDialog^ userValueDialog;
You’ll also need an #include directive for UserValueDialog.h in the Form1.h header file.
Adding the following line to the constructor creates the dialog object:
userValueDialog = gcnew UserValueDialog;
If you double-click the OK button in the UserValueDialog form, you’ll create the Click event handler
for the button. This function retrieves the value entered in the
TextBox control, and checks that the
value is within the limits and is different from the current set of values. If the value is not valid for any
reason, the function displays a message box. Here’s how you implement that:
System::Void OK_Click(System::Object^ sender, System::EventArgs^ e)
{
::DialogResult result; // Stores return value from Show()
if(String::IsNullOrEmpty(textBox->Text)) // Chheck for null or empty string
{
result = MessageBox::Show(this,
L”No input - enter a value.”,
L”Input Error”,
MessageBoxButtons::RetryCancel,
MessageBoxIcon::Error);
if(result == ::DialogResult::Retry) // If Retry button clicked
DialogResult = ::DialogResult::None; // prevent dialog from closing
else // otherwise
DialogResult = ::DialogResult::Cancel;// close the dialog.
return;
}
int value = Int32::Parse(textBox->Text); // Get text box value
1082

Chapter 21
24_571974 ch21.qxp 1/20/06 11:47 PM Page 1082

×