You need to be able to update the properties because the Clickevent handler for the Limits > Resetmenu item changes the limits, and you want the ListBoxobjects to have whatever is the cu
Trang 1You have more than one control in the dialog box that you could use to permit a limit value to beentered You can use a ListBoxcontrol to enable the user to select from a list of possible values, so youcan try that here You should add two Labelcontrols to the dialog box form with two ListBoxcontrolsalongside, as shown in Figure 21-21.
The GUI for the dialog box is complete, but to make it do what you want you are back in coding modeagain You can start with the code that populates the ListBoxcontrols with limit values
Adding a List to a ListBox
The list that a ListBoxcontrols is a set of objects that are stored as handles of type Object^,so anykind of object can be stored in the list In the example you want to store a set of integer limit values ineach list box, and for the most part you are able to rely on autoboxing and unboxing to convert values
of type intto and from objects of type Int32whenever necessary The Itemsproperty for a ListBoxobject returns a reference to a collection of the objects in the list box; this collection has an Add()methodthat adds an object that you pass as the argument to the list AListBoxobject has a large number ofproperties including the Enabledproperty that has the value truewhen the user can interact with thelist box and the value falsewhen interaction is to be inhibited
The basic process for loading up the list for a list box is the same for both ListBoxcontrols, so youcould code a private function member of the LottoLimitsDialogclass that is generalized to add arange of integers to a list box:
1058
Chapter 21
Trang 2void SetList(ListBox^ listBox, int min, int max, int selected){
listBox->BeginUpdate(); // Suppress drawing the listboxfor(int n = min ; n <= max ; n++)
listBox->Items->Add(n);
listBox->EndUpdate(); // Resume drawing the list boxlistBox->SelectedItem = Int32(selected);
} The arguments to the SetList()function are the list box for which the list is to be added, the minimumand maximum integers in the range to be added, and the integer that is to be selected in the list box Thefunction adds integers from minto maxinclusive to the list box using the Add()function for the collec-tion object that is returned by the Itemsproperty for the ListBoxobject 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 forthe SelectedItemproperty 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 Form1object; the event handler for the menu items haveresponsibility for retrieving the limit value and storing it in the Form1object One way to do this is toadd a couple of private members to the LottoLimitsDialogclass to store the upper and lower limitvalues and then add public properties to the class to make the values available externally Adding thefollowing code to the LottoLimitsDialogclass definition does that:
private:
int lowerLimit; // Lower limit from controlint upperLimit; // upper limit from controlpublic:
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
Trang 3You need to be able to update the properties because the Clickevent handler for the Limits > Resetmenu item changes the limits, and you want the ListBoxobjects 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 ListBoxobjects to reflect the new limits
You can now create two public member functions in the LottoLimitsDialogclass that sets up the twoListBoxcontrols:
SetList(lottoUpperList, min, max, selected);
upperLimit = selected;
}Each function uses the SetList()function to set the range of values in the corresponding ListBoxobject and then sets the selectedvalue in the member for storing the limit
Handling the Dialog Button Events
Add an event handler function for the Clickevent for the OK Buttonobject, so return to the Designtabfor the LottoLimitsDialogform and double-click the OK button to add the skeleton code
You don’t need to add a handler for the Clickevent for the Cancelbutton The effect of clicking thebutton is to close the dialog box and no further action is required
You can implement the handler for the Clickevent for the OKbutton like this:
System::Void lottoOK_Click(System::Object^ sender, System::EventArgs^ e)
1060
Chapter 21
Trang 4Controlling the State of the ListBox ObjectsThe same dialog object is used in the response to the Clickevents for both the Limits > UpperandLimits > Lowermenu items, but you don’t want to allow both list boxes to be changed in either case.For the Uppermenu item event you’ll want the selection of a lower limit to be inhibited, and for theLowermenu item you’ll want the list box for the upper limit to be inhibited You could add a couple ofpublic function members to the LottoLimitsDialogclass to make this possible Here’s the function toset the state of the ListBoxobjects for the Uppermenu item:
void SetUpperEnabled(){
lottoUpperList->Enabled = true; // Enable upper list boxlottoLowerList->Enabled = false; // Disable lower list box}
You set the Enabledproperty for the lottoUpperListobject to trueto allow the user to interact with
it Setting the Enabledproperty for lottoLowerListto falsemakes it read-only
For the Lowermenu item you do the reverse:
void SetLowerEnabled(){
lottoUpperList->Enabled = false; // Disable upper list boxlottoLowerList->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 youdon’t yet have a dialog object The application window object takes care of that
Creating the Dialog ObjectThe Form1class constructor can create the dialog object It can also initialize the ListBoxobjects in thedialog 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 Form1constructor:
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 statementscall the functions that initialize the lists in the ListBoxobjects The maximum value in the ListBoxobject 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 mum for the lower limit must be 44 — if it was any higher you could not create six different values Thesame 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 lottoUserMinimumand lottoUserMaximumvalues
maxi-1061
Applications Using Windows Forms
Trang 5Because you refer to the LottoLimitsDialogclass name in the Form1class constructor, you’ll need toadd an #includedirective 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 Clickevent handlers for the UpperandLowermenu items in the Limitsmenu To display a dialog box as a modal dialog box, you call theShowDialog()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 Clickevent handler functionslike this:
System::Void lowerMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
// Update user limits from dialog propertieslottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}}
// Update user limits from dialog propertieslottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}}
Trang 6object by the Clickevent handler for the lottoOKbutton 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 accessdata that might have been changed in the dialog box, you need another way to do it Adding a handlerfunction for the Closingevent for the dialog form is one possibility; another would be to deal withtransferring the data in the handler for the button that closes the dialog box
The ShowDialog()function returns a value of the enumeration type DialogResultand you store this inthe local variable, result The return value from the ShowDialog()function indicates which button inthe dialog was clicked, and if the value is the enumeration constant ::DialogResult::OK, it indicates thatthe OKbutton was clicked Thus the code in each handler function updates only the lottoUserMaximumand LottoUserMinimumfields when the OKbutton was used to close the dialog
Note the use of the ::operator in the type specification ::DialogReultand in the expression::DialogResult::OK The scope resolution operator is necessary preceding the DialogResultname
to distinguish the name of the enumeration at global scope from the property with the same name that
is a member of the Form1class
Of course, you could access the DialogResultproperty for the lottoLimitsDialogobject directly, soyou could write the ifstatement as:
if(lottoLimitsDialog->DialogResult == ::DialogResult::OK){
// Update user limits from dialog propertieslottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}The former version is better because it is obvious that you are checking the value returned by theShowDialog()function
The complete set of constants that the DialogResultenumeration defines is as follows:
The DialogResultproperty value that is set for a dialog box determines whether or not the dialog boxcloses If the value is set to None,the dialog box does not close You can set the property to this value whenyou want to prevent the dialog box from closing for some reason — if the input is not valid, for instance
At the moment, the Clickevent handler for the OKbutton does not validate the input values It is rently quite possible to set the lower and upper limits to values that make it impossible to assign sixunique values for the lottery entry You can use the DialogResultproperty for the form to deal withthe problem
cur-Validating the InputThe 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 Clickevent handler for the OKbutton in the dialog class to check for this:
1063
Applications Using Windows Forms
Trang 7System::Void lottoOK_Click(System::Object^ sender, System::EventArgs^ e)
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);
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 MessageBoxButtonsenumeration defines the following values:
OK, OKCancel, YesNo, YesNoCancel, RetryCancel, AbortRetryIgnore
MessageBoxIcon An enumeration constant specifying the icon to appear in the message
box The MessageBoxIconenumeration defines the following values:Asterisk, Exclamation, Error, Hand, Information, None, Question, Stop, Warning
1064
Chapter 21
Trang 8There are a significant number of overloaded versions of the static Show()function that range from thevery simple with a single parameter of type String^to the rather more complicated with up to 10parameters.
If you compile and execute the example and set the limits inappropriately, you’ll see a window similar tothat shown in Figure 21-22
dia-System::Void lottoOK_Click(System::Object^ sender, System::EventArgs^ e) {
1065
Applications Using Windows Forms
Trang 9::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;
elseDialogResult = ::DialogResult::Cancel;
1066
Chapter 21
Trang 10Handler the Reset Menu Item EventYou can implement the event handler for the Resetmenu item like this:
System::Void resetMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
if(lottoTab->Visible){
// Reset user limits for LottolottoUserMaximum = lottoUpperLimit;
lottoUserMinimum = lottoLowerLimit;
lottoLimitsDialog->UpperLimit = lottoUpperLimit;
lottoLimitsDialog->LowerLimit = lottoLowerLimit;
}else if(euroTab->Visible){
// Reset user limits for EuromillionseuroUserMaximum = euroUpperLimit;
You can now recompile the program and try changing the limits for the Lotto entry A typical applicationwindow is shown in Figure 21-24
Figure 21-24
1067
Applications Using Windows Forms
Trang 11As you see, you automatically get a scrollbar for scrolling through the list of items in a list box Note thatscrolling to a given item does not select it You must also click on the item to select it before clicking the
OKbutton 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+Ato displaythe Add New Itemdialog box and select the UIcategory and the Windows Formtemplate; the nameshould 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
You can add OKand Cancel buttons to the dialog form next Set the Textproperty values for the buttons
to “OK”and “Cancel”and the (Name)property values to euroOKand euroCancel,respectively Youshould also set the DialogResultproperty values to OKand Cancel With the buttons defined, you canreturn to the properties for the dialog form and set the AcceptButtonand CancelButtonproperty val-ues to euroOKand euroCancel,respectively
In the interests of getting experience of a wider range of controls, you’ll forego consistency in the cation, and you won’t use ListBoxcontrols to handle the input as you did in the first dialog box In thisdialog box you need to provide for the entry of upper and lower limits for the set of five values as well
appli-as the set of two stars It won’t make for a very elegant implementation, but to maximize the variety ofcontrols you work with you’ll use NumericUpDowncontrols for the former and ComboBoxcontrols forthe latter You can add these controls together with associated Labelcontrols to the dialog form witheach group of controls placed within a GroupBoxcontrol, as illustrated in Figure 21-25 Obviously
“you’ll need to add the GroupBoxcontrols first and then place the other controls within them
To identify the function of the controls within each group box, the value for the Textproperty for theupper group box has been set to “Set Values Limits”and that of the lower group box “Set StarsLimits” You won’t be accessing the GroupBoxobjects in the code so the (Name)property values forthese are of no importance You can set the value of the Textproperty for each Labelcontrol as shown
1068
Chapter 21
Trang 12property for the upperValuesLimitcontrol to 49; this is the value displayed initially in the control Ifyou also set the ReadOnlyproperty value for each of the NumericUpDowncontrols to True,this preventsthe entry of a value from the keyboard You are using the NumericUpDowncontrol very simply here Youcan change the up down increment by setting the Incrementproperty value The Incrementproperty
is of type Decimalso you can set this to non-integral values, too
Figure 21-25
You can set the values of the (Name)property for the ComboBoxcontrols in the lower group box tolowerStarsLimitsand upperStarsLimits You can enter values to be displayed in a ComboBoxquiteeasily Click the small arrow at the top right of the leftmost ComboBoxcontrol to display the menu shown
Trang 13Select the Edit Itemsmenu item at the bottom of the menu to display the dialog window for theString Collection Editorshown in Figure 21-27.
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 dialogbox for Lotto limits You can add some new data members to the EuroLimitsDialogclass to hold theuser limit values first:
1070
Chapter 21
Trang 14lowerValuesLimits->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
Trang 15The get()function for each property returns the value of the corresponding private member of the dialogclass 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 SelectedIndexproperty value is the index to theselected item You set this using the FindString()function for the ComboBoxcontrol that returns theindex value for the first occurrence of the argument in the control’s collection of items The value at thisposition is displayed initially in the control
Add a Clickevent handler for the OKbutton in the EuroLimitsDialogclass by double-clicking thebutton in the Design window You won’t need to implement a handler for the Cancelbutton You canimplement the OKbutton 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 clickedDialogResult = ::DialogResult::None; // prevent dialog from closingelse // Messag box Cancel clickedDialogResult = ::DialogResult::Cancel; // so close the dialogreturn;
}
// 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
Trang 16if(result == ::DialogResult::OK) // If message box OK clickedDialogResult = ::DialogResult::None; // prevent dialog from closingelse // Message box Cancel clickedDialogResult = ::DialogResult::Cancel; // so close the dialog}
// Store the new limitslowerValuesLimit = valuesLower;
upperValuesLimit = valuesUpper;
lowerStarsLimit = starsLower;
upperStarsLimit = starsUpper;
}The Valueproperty for a NumericUpDowncontrol returns a value of type Decimal To convert this totype Int32you pass it as the argument to the static ToInt32()function in the Decimalclass The valuethat this function returns is automatically unboxed so that it can be stored in the variable of type int.The value returned by the SelectedItemproperty for a ComboBoxcontrol 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 valuerecorded in the dialog object; if it isn’t null, you store the value represented by the SelectedItemprop-erty You can’t store the value directly, but calling the ToString()function for the object produces astring representation of the object that you are then able to convert to type intusing the static Parse()function in the Int32class
You will need a private member of the Form1class that stores a handle to the new dialog box:
private:
EuroLimitsDialog^ euroLimitsDialog; // Dialog to set Euromillions limitsYou can add the following statements to the end of the code in the Form1class constructor to create thedialog object and update the properties for the stars limit values:
euroLimitsDialog = gcnew EuroLimitsDialog;
euroLimitsDialog->LowerStarsLimit = euroStarsLowerLimit;
euroLimitsDialog->UpperStarsLimit = euroStarsUpperLimit;
By setting the LowerStarsLimitand UpperStarsLimitproperties for the dialog object, you ensurethat the ComboBoxcontrols show these values when the dialog box is initially displayed If there is noselected item set for a ComboBoxcontrol, it displays nothing initially
Don’t forget to add the #includedirective for the EuroLimitsDialogclass definition to Form1.h:
#include “EuroLimitsDialog.h”
Disabling Input ControlsWhen the Limits > Uppermenu item is clicked, you want to prevent the input for a lower limit beingentered, and when the Limits > Lowermenu item is selected, you want to prevent input for an upperlimit value You can add a couple of member functions to the EuroLimitsDialogclass to make thispossible:
1073
Applications Using Windows Forms
Trang 17Updating the Limits Menu Item Handlers
The last step to complete the support for entering limits for the Euromillions lottery is to update theClickevent handlers in the Form1class for the items in the Limitsmenu The handler for the Uppermenu item should be modified as follows:
System::Void upperMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
lottoUserMaximum = lottoLimitsDialog->UpperLimit;
lottoUserMinimum = lottoLimitsDialog->LowerLimit;
}}
Trang 18euroStarsUserMinimum = euroLimitsDialog->LowerStarsLimit;
}}}The local variable resultis used in both ifstatements, so it is now declared at the beginning of thefunction 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 byclicking the OKbutton, you store the results available through the properties of the dialog object
The changes to the handler for the Clickevent for the Lowermenu 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){
Implementing the Help | About Menu Item
This is easy now that you know about the MessageBoxclass You can just show a message box when theHelp > Aboutmenu 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”,
Trang 19When 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 valuemust 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 inthe new value being on a different button, but that’s likely to be better that not having the values insequence
The process for handling a button click is going to be the same for all the buttons, so you’ll be able toeconomize on code by creating a generalized function to do the work You can define a private functionmember of the Form1class that generates a new value for a given Buttonobject 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++)
Trang 20if(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 valueif(IsValid(newValue, values, values->Length)) // If it’s OK
break; // end the loop}
values[index] = newValue; // Store the new value at indexArray::Sort(values); // Sort the valuefor(int i = 0 ; i < values->Length ; i++) // and set the valuesbuttons[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 upperlimits for the values The current values of the buttons in the array are stored in the values array in thefirst loop This loop also finds the index value in the buttonsarray for the Button^handle that is thefirst argument You need this so you know which element of the valuesarray is to be replaced by a new value
The new value is created in the indefinite forloop 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 valuesarray You then sort the elements in the valuesarray before storing them as the values for theTextproperties for the buttons in the buttonsarray You’ll be able to use this function for dealing withthe Clickevents for all of the buttons
If you have not already done so, double-click the first button on the Lotto tab to generate a Clickeventhandler function for it You can edit the name of the handler function by opening the Propertiestabfor the button, selecting the Eventsbutton, and changing the value for the Clickevent When youpress Enter, the code is updated with the new name I changed the value to lottoValue_Click.You can amend the Clickevent handler to call the SetNewValue()function you have just added to theForm1class:
System::Void lottoValue_Click(System::Object^ sender, System::EventArgs^ e) {
Button^ button = safe_cast<Button^>(sender);
// Create the array of button handlesarray<Button^>^ buttons = {lottoValue1, lottoValue2, lottoValue3,
lottoValue4, lottoValue5, lottoValue6};
// Replace the value on buttonSetNewValue(button, buttons, lottoUserMinimum, lottoUserMaximum);
}
1077
Applications Using Windows Forms
Trang 21The availability of the SetNewValue()function makes this handler function very simple The first 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 Youthen just assemble the handles for the buttons in an array and call the new function — job done!
state-You still have to deal with the Clickevent for the other buttons on the Lotto tab, but this doesn’t requireany more code Open the Propertieswindow for the second button and then click the Eventsbutton
If you click the Clickevent value, you’ll see a list of the existing event handlers as shown in Figure 21-29;
if you select lottoValue_Clickfrom the list, the event handler for the first button will be registered asthe 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 Clickevent for any of the buttons on the Lotto tab
The Clickevent handlers for the buttons on the Euromillions tab are going to be very easy Double-clickthe first of the five buttons in the Valuesgroup to create the event handler Open the Properties windowfor the button and change the value for the Clickevent to euroValue_Click You can then modify thecode for the handler like this:
1078
Chapter 21
Trang 22System::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 fivebuttons in the values group, and the SetNewValue()function does the rest If you open the Propertieswindow for each of the remaining four buttons in the group, you can select this function to respond to theClickevent for each of them Be sure you select euroValue_Clickand 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 Clickevent for the second button to be euro_StarClickand you are done Ifyou 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 clicksthis item, the program should display a dialog box that allowed a suitable value to be entered Click thename of the context menu in the Design tab for Form1and then double-click the menu item to create theClickevent handler
The first problem is to determine which group of buttons was clicked to cause the event Each group onbuttons is in its own GroupBoxcontrol, and the GroupBoxclass has a Controlsproperty that returns areference to an object of type Control::ControlCollectionthat represents the collection of controls
in the group box The Control::ControlCollectionclass defines the Contains()function thatreturns trueif the control that you pass as the argument is within the collection and falseotherwise.Thus you have a way to determine to which group of buttons the button causing the Clickeventbelongs 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
Trang 23// The button is in the Values group
System::ComponentModel::CancelEventArgs^ e) {
contextButton = safe_cast<Button^>(buttonContextMenu->SourceControl);
}
This casts the senderhandle to type Button^and stores it in the contextButtonmember of the Form1class 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 contextButtonvariable as a pri-vate member of the Form1class:
private:
Button^ contextButton; // Button that was right-clicked for context menuAll 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 Chooseitem being clicked can be the same whichever group of tons is involved, and it could work something like the following:
but-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 dialogoperation 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+Ato display the Add New Itemdialog box; then select UIas the category andWindows Formas the template Enter the name as UserValueDialogand click the Addbutton You can
1080
Chapter 21
Trang 24now open the Propertieswindow for the form by pressing F4and setting the property values to make
it a dialog box Set the ControlBox, MinimizeBox, and MaximizeBoxproperty values to False, and setthe Textproperty to “User Value Input.”
Add OKand Cancelbuttons to the dialog form as well as a Labelcontrol and a TextBoxcontrol, asshown in Figure 21-30
Figure 21-30
Set the Textand (Name)property values for the OK button to OK,and the value for the DialogResultproperty should be set to OK The values for the Text, (Name), and DialogResultproperties for theCancel button should be Cancel Set the value of the (Name)property for the TextBoxcontrol to betextBoxand the value for the TextAlignproperty as Center The (Name)property for the Labelcon-trol can be label,and the Textproperty can be anything you like because you’ll change this in thecode to suit the circumstances
You can now display the properties for the dialog form once again and set the values for theAcceptButtonand CancelButtonproperties to OKand Cancel,respectively
Developing the Dialog ClassThe value entered in the TextBoxcontrol must be available to the Form1object, so add a property to theUserValueDialogclass 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 toknow what the current values on the buttons are to ensure they are not duplicated You could add threefurther property members to the UserValueDialogclass to store the data:
1081
Applications Using Windows Forms
Trang 25property int LowerLimit;
property int UpperLimit;
property array<int>^ Values; // Current button values
The Form1object needs to be able to change the value of the Textproperty for the labelcontrol, ing on the limits in effect for button values when the dialog box is displayed; you can add a public memberfunction to the UserValidDialogclass to do this:
private: UserValueDialog^ userValueDialog;
You’ll also need an #includedirective for UserValueDialog.hin the Form1.hheader file
Adding the following line to the constructor creates the dialog object:
userValueDialog = gcnew UserValueDialog;
If you double-click the OKbutton in the UserValueDialogform, you’ll create the Clickevent handlerfor the button This function retrieves the value entered in the TextBoxcontrol, and checks that thevalue is within the limits and is different from the current set of values If the value is not valid for anyreason, 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
Trang 26bool valid = true; // Indicator for valid entryfor each(int n in Values) // Check input against current valuesif(value == n) // If it’s the same
{valid = false; // it is invalid
break; // Exit the loop }
// Check limits and result of previous validity checkif(!valid || value < LowerLimit || value > UpperLimit){
result = MessageBox::Show(this,
L”Input not valid.” +L”Value must be from “ + LowerLimit +L” to “ + UpperLimit +
L”\nand must be different from existing values.”,L”Input Error”,
MessageBoxButtons::RetryCancel,MessageBoxIcon::Error);
if(result == ::DialogResult::Retry)DialogResult = ::DialogResult::None;
elseDialogResult = ::DialogResult::Cancel;
}elseValue = value; // Store the input in the property}
A message box is displayed if the Textproperty for the text box is null or an empty string The messagebox shows an error message and has Retryand Cancelbuttons instead of OK and Cancel for a change IfRetryis clicked, the user wants another go at input so you prevent the dialog box from closing by settingits DialogResultproperty to ::DialogResult::None The only other possibility is that the user clickedCancelin the message box, in which case you set the DialogResultproperty for the dialog object to::DialogResult::Cancel,which has the same effect as clicking the Cancelbutton for the dialog box.The Textproperty for the TextBoxcontrol returns a handle of type String^ You convert this to aninteger by passing the handle to the static Parse()function in the Int32class You compare the valuefrom the text box with the elements from the valuesarray that represent the current set of button val-ues The new value should be different from all of these, so if you find one that is the same, you setvalidto falseand exit the loop
The condition for the ifstatement following the for eachloop checks the value against the limits andthe current value of validby ORing the conditions together If any of the three expressions are false,the condition is false,and you display a message box This works in the same way as the previousmessage box and displays an error message and Retryand Cancelbuttons If the value does indeedturn out to be valid you store it in the Valueproperty for the dialog object ready for retrieval by theevent handler in the Form1object that started the whole process off
Handling the Click Event for the ChooseMenuYou are now going to complete the skeleton of the chooseValue_Click()handler function using thecapabilities that you have added to the UserValueDialogclass The handle for the button that was
1083
Applications Using Windows Forms
Trang 27right-clicked is already stored in the contextButtonmember because the buttonContextMenu_Opening()handler that you added earlier is executed first.
System::Void chooseValue_Click(System::Object^ sender, System::EventArgs^ e) {
array<int>^ values; // Array to store current button valuesarray<Button^>^ theButtons; // Handle to aray of buttons
// Check if the button is in the lottoValues group box
if(lottoValues->Controls->Contains(contextButton))
{
// the button is from the lotto group
array<Button^>^ buttons = {lottoValue1, lottoValue2, lottoValue3,
lottoValue4, lottoValue5, lottoValue6};
theButtons = buttons; // Store array handle at outer scopevalues = GetButtonValues(buttons); // Get array of button values// Set up the dialog ready to be shown
userValueDialog->Values = values = GetButtonValues(buttons);
// The button is in the Values group
array<Button^>^ buttons = {euroValue1, euroValue2, euroValue3,
euroValue4, euroValue5};
theButtons = buttons; // Store array handle at outer scopevalues = GetButtonValues(buttons); // Get array of button values// Set up the dialog ready to be shown
// The button is in the Stars group
array<Button^>^ buttons = { euroStar1, euroStar2 };
theButtons = buttons; // Store array handle at outer scopevalues = GetButtonValues(buttons); // Get array of button values// Set up the dialog ready to be shown
userValueDialog->Values = values;
userValueDialog->LowerLimit = euroStarsUserMinimum;
userValueDialog->UpperLimit = euroStarsUserMaximum;
userValueDialog->SetLabelText(euroStarsUserMinimum, euroStarsUserMaximum);}
1084
Chapter 21
Trang 28// Display the dialogif(userValueDialog->ShowDialog(this) == ::DialogResult::OK){
// Determine which button value should be replacedfor(int i = 0 ; i<theButtons->Length ; i++)if(contextButton == theButtons[i]){
values[i] = userValueDialog->Value;
break;
}Array::Sort(values); // Sort the values// Set all the button values
for(int i = 0 ; i<theButtons->Length ; i++)theButtons[i]->Text = values[i].ToString();
}}You first define two array variables, one to hold the buttons, the other to hold the button values Youneed to declare these here because these arrays are created within one or other of the ifstatementblocks and you’ll want to access them outside the ifblocks
The first three ifstatements determine which group box contains the button that was right-clicked to openthe context menu The processes within the three ifblocks are essentially the same, but the arrays createdwill be different The array of buttons is created from the variables holding the handles to whichever set ofbuttons the contextButtonbelongs The array handle is then stored in theButtonsto make it accessible
in the outer scope You then call a function you have yet to add, GetButtonValues(), that returns anarray containing the integer values from the buttons Finally, in the ifblock you set the three properties forthe dialog box object and call its SetLabelText()function to set the label text according to the applicablelimits The contextButtonhas to belong to one of the three group boxes as these are the only buttons thathave the context menu available
When one or other of the if blocks has executed, you display the dialog box by calling its ShowDialog()function in the condition for the fourth ifstatement If the ShowDialog()function returns
::DialogResult::OK,you execute the code in the ifblock This first determines which button shouldhave its value replaced by comparing contextButtonhandle with the handles in the theButtonsarray As soon as you find a match, you replace the corresponding element in the valuesarray with the new valueand exit the loop After sorting the values, you update the Textproperty for each of thebuttons in the theButtonsarray and you are done
The implementation of the GetButtonValues()function in the Form1class looks like this:
// Creates an array of button values from an array of buttonsarray<int>^ GetButtonValues(array<Button^>^ buttons)
{array<int>^ values = gcnew array<int>(buttons->Length);
for(int i = 0 ; i<values->Length ; i++)values[i] = Int32::Parse(buttons[i]->Text);
Trang 29Here you create an array of integer values the same length as the array of button handles that is passed
as the argument You then populate the values array with the intequivalents of the string returned bythe Textproperties of the buttons, and return the handle to the values array
After compiling the project once more, you should have a fully functional application You can generatelottery entries for a variety of lotteries with the range of values constrained or not You can also elect togenerate new random individual values in an entry or choose your own It has always worked but neverwon for me; of course, working is a measure of success
Summar y
In this chapter, you assembled a Windows Form application that uses the controls that you are mostlikely to need in the majority of programs It should be apparent that Windows Forms programs aregeared exclusively to using the Design capability All the code for a class goes into the class definition,
so with a very complex form the class is many lines of code With a production application the code sists of a number of large classes that are rather unstructured, and difficult to modify and maintain at the code level You should therefore always use the Design capability and the Properties window toexpedite changes wherever you can and whenever you need to access the code, use Class View to findyour way around
con-The key points to keep in mind from this chapter include:
❑ An application window is a form, and a form is defined by a class derived from the
System::Formclass
❑ A dialog window is a form that has its FormBorderStyleproperty value set to FixedDialog
❑ A dialog box can be created as a modal dialog by calling its ShowDialog()function or as amodeless dialog by calling its Show()function
❑ You can control whether or not a dialog box closes by setting the DialogResultproperty valuefor the dialog object
❑ AComboBoxcontrol combines the capabilities of a ListBoxand a TextBoxand allows selection
of an item from a list or a new item to be entered from the keyboard
❑ ANumericUpDowncontrol allows the entry of numeric data by stepping through values within
a given range with a given step increment
❑ You can add the definition of a Clickevent handler for a control by double-clicking the control
in the Form Design tab
❑ You can specify an existing function to be a handler for a given event for a control through theProperties window Clicking the Eventsbutton in the Propertieswindow displays the list ofevents for a control
❑ You should only change the names of automatically generated class members through thePropertieswindow — not directly using the Code editor
1086
Chapter 21
Trang 30ExercisesYou can download the source code for the examples in the book and the solutions to the following exer-cises from http://www.wrox.com.
1. Modify Ex21_01so that it displays a dialog box that you created as a dialog form when theHelp | Aboutmenu item is clicked
2. Modify Ex21_01so that the dialog box that displays for the Choosecontext menu item uses aListBoxcontrol instead of the text box and displays the complete set of legal values that can bechosen
3. Investigate the properties and functions available for the WebBrowsercontrol and modify
Ex21_01to allow a URL to be entered through a TextBoxso that the WebBrowsercontrol plays the page at the URL that was entered
dis-1087
Applications Using Windows Forms
Trang 32an existing database In this chapter, you will learn about:
❑ What kind of classes are involved in encapsulating a data source
❑ How you can use the DataGridViewcontrol to display your own data
❑ How you can customize the appearance of a DataGridViewcontrol
❑ What the function of the BindingSourcecomponent is and how you use it with aDataGridViewcontrol
❑ How you use a BindingNavigatorcontrol to navigate data from a source managed
Wor king with Data Sources
A data source is any source of data for your application; relational databases, Web services thataccess data, and objects can all be data sources When you are developing an application that willwork with an existing data source, you will usually need to identify the data source within yourproject You’ll do this through the Data Sourceswindow that is displayed when you select Data > Show Data Sourcesitem from the main menu or when you press Shift+Alt+D
Trang 33Chapter 22
A data source is represented by a class object, so inevitably adding a data source to your project addsdefinitions for a number of classes Take a brief look at what they are
Data Source A data source is defined by a class that is derived from the DataSetclass that
is defined in the System::Datanamespace This class encapsulates an memory cache of all the data from the database that is accessible in your project
in-Database Tables Each table in the database is defined by a nested class in the DataSetclass
that represents the database, and the class that defines a table is derived fromthe System::Data::DataTableclass The classes representing tables alsodefine events that signal changes to the data in the table and propertie, makingeach of the values of the current database record available
Each table in a data source is identified by a member in the DataSetclass that
is a handle to the corresponding DataTableobject
Table Columns Each column in a given database table is identified by a member of the Data
Tableclass that defines the table The members representing columns are oftype System::Data::DataColumnand define the characteristics of the col-umn, such as the column name and the type of data in the column These char-
acteristics are collectively referred to as the schema for the column.
Table Rows A row in a table is represented by an object of type System::Data::DataRow
ADataRowobject contains the data in a row and has as many data items asthere are columns in the DataTableobject
Clearly, with a database that involves several tables that each have a number of columns, you are going
to see quite a lot of code generated to represent a data source; certainly, tens of thousands of lines of codeare not uncommon in a practical context
The classes I have identified in the previous table are solely to encapsulate data from a data source; they
do not provide the mechanism for connecting to a data source such as a database and accessing the data
within it That capability is provided by a component class called a table adapter that will be generated
automatically A table adapter establishes a connection to a database and executes the commands or SQLstatements that operate on the database There is one table adapter class for each DataTablemember inthe DataSetobject, so if your application is going to work with three tables from a database, there arethree table adapter classes defined A table adapter object populates a DataTablemember of a DataSetobject with data and can update the table in the database when required
Accessing and Displaying Data
There are three components defined in the System::Windows::Formsnamespace that are designed to
be used together for accessing and displaying data in a Windows Forms application:
Trang 34Accessing Data Sources in a Windows Forms Application
Component Description
DataGridView This control can display virtually any kind of data in a rectangular
grid You can use this control quite independently of the other twocomponents
BindingSource This component is used to encapsulate data from a data source The
component can manage accessing and updating the data source, andcan be used as the vehicle for displaying data in a DataGridViewcontrol
BindingNavigator This control provides a toolbar containing controls for navigating and
manipulating data from a data source, typically a data source lated in a BindingSourcecontrol
encapsu-The BindingSourcecomponent is not a control as it has no graphical representation that a user caninteract with, but it’s designed to complement and work with the DataGridViewcontrol and theBindingNavigatorin database applications The BindingSourcecomponent provides the communi-cations with the data source necessary to execute queries and update commands, the DataGridViewcontrols provides the user interface for viewing and entering the data and the BindingNavigatorcon-trol provides a toolbar that simplifies data navigation Using the BindingNavigatorcontrol is optional
If you prefer, you can change records programmatically yourself
Although these three components are designed to work as a team, the DataGridViewcontrol is a ularly useful tool in its own right, as you can use it quite independently from the other two It provides
partic-an astonishing rpartic-ange of capabilities for chpartic-anging the visual appearpartic-ance of the grid that displays the data.You’ll explore some of the ways in which you can customize the DataGridViewcontrol before goinginto how you can use it combined with the BindingSourceand BindingNavigatorcomponents
Note that you can also use the SqlConnection, SqlDataAdapter, and DataSetcontrols for accessing a data source If you want to use these controls, then you may need to add them to the ToolBox yourself You do this by selecting Tools > Choose ToolBox Items from the main menu and checking the controls in the list that you want to have added to the ToolBox.
Using a DataGridV iew ControlThe DataGridViewcontrol enables you to display and modify a rectangular array of data from a widerange of data sources You can also use the DataGridViewcontrol to display almost any kind of datathat originates directly in a program Under the covers it is a complex control that provides an enormousamount of flexibility, and you can take advantage of its many features through its many properties, func-tions, and events At the same time, the DataGridViewcontrol can be remarkably easy to use You canignore the complexity of the internals and use it through the Form Design capability that takes care of allthe basic detail for you You’ll see later in this chapter how you can produce a complete working pro-gram example to access the Northwind database with no programming at all on your part; the wholeprogram will be generated through the Form Design capability and by setting properties for componentsused in the project
Trang 35The data in a DataGridViewcontrol is displayed in a rectangular array of cells that you can envisage as
a collection of rows or as a collection of columns Each column of cells has a header cell at the top thattypically contains text that identifies it, and each row has a row header cell at the beginning, as shown inFigure 22-1
Figure 22-1
You reference rows and columns of cells through properties of the DataGridViewcontrol object TheRowsproperty returns a value of type DataGridRowCollectionthat is a collection of all the rows, andyou refer to a particular row using an index, as illustrated in Figure 22-1 Similarly, the Columnsprop-erty for the control returns a value of type DataGridViewColumnCollectionthat you can also index
to reference a particular column Rows and columns are indexed from zero The Cellsproperty for aDataGridRowCollectionobject represents a collection containing the cells in the row, and you canindex the Cellsproperty to access a specific cell in a row Figure 22-1 shows an example of how you reference the fourth cell in the third row
The number of rows is available as the value of the RowCountproperty for the control, and the
ColumnCountproperty returns the number of columns Initially, when the control is not bound to a datasource, it will have no columns or rows You can set the number of columns and or the number of rows
by setting property values for the control, but when you use the control to display data from a datasource, this is taken care of automatically
You can use the DataGridViewcontrol in three different modes:
gridCntrl->Columns
references thecollection of columns
gridCntrl->Rows[2]>Cells[3]
references the 4thcell in the 3rd row
gridCntrl->Rows
references thecollection of rows
These are thecolumn headers
There are therow headers
gridCntrl->ColumnCount
is the number of columns
DataGridView^ gridCntrl = gcnew DataGridView; // Creates the control
Trang 36Accessing Data Sources in a Windows Forms Application
unbound mode In unbound mode you transfer the data to the control yourself, typically using
the Add()function for the Rowsproperty for the control You would use thismode for displaying relatively small amounts of data
bound mode In this mode you identify a source for the data that is to be displayed by
set-ting a value for the DataSourceproperty of the control
virtual mode In virtual mode you connect the control to a data cache in memory that you
fill with data from a separate data source You would use this mode to displaydata from a source where you want to manage data access in order to opti-mize performance
In unbound mode, you can use the DataGridViewcontrol to display any data in your application thatcan be displayed in a grid This makes it a very convenient tool for displaying data in many differentkinds of applications The next section looks into using the control in unbound mode in a little moredepth
Using a DataGridV iew Control
in Unbound ModeThe data in DataGridViewcontrol is stored in a rectangular arrangement that is identified by the Rowsand Columnsproperties for the control In unbound mode you’ll add the data to the control using theAdd()function for the Rowsproperty, but before you can add rows to the control, the columns need to
be defined, at least to establish how many items are in a row Setting the ColumnCountproperty for thecontrol programmatically sets how many columns there are and determine that the control is to work inunbound mode The following statements create a control that you reference using the handle
dataGridViewand then set the number of columns to 3:
DataGridView^ dataGridView = gcnew DataGridView;
dataGridView->ColumnCount = 3; // Set number of columnsYou can optionally label the columns in the control by specifying headers to identify the data in each col-umn by setting the Nameproperty for each column Here’s how that could be done:
in the next working example
The value returned by the Rowsproperty is a collection of type DataGridViewRowCollection,andthis type is defined in the System::Windows::Formsnamespace The Countproperty for the collectionreturns the number of rows and there is also a default indexed property to return the row at a givenindex position The collection of rows has a large number of functions; I won’t go through them all here,but here are a few of the most useful ones for adding and deleting rows:
Trang 37Chapter 22
InsertCopy() Inserts a copy of the row specified by the first argument at
the position specified by the second argument
Remove() Removes the row specified by the argument, which is of
type DataGridViewRow^.RemoveAt() Removes the row specified by the index value you supply
as the argument
The Add()function for the value returned by the Rowsproperty comes in four overloaded versions thatenable you to add a row of data to the control in a variety of ways
Add(int rowCount) Adds rowCountnew rows to the collection An exception of
type System::ArgumentOutOfRangeExceptionis thrown
if rowCountis zero or negative
Add(DataGridViewRow^ row) Adds the row specified by the argument ADataGrid
ViewRowobject contains the collection of cells in a row aswell as the parameters that determine the appearance of thecells in the row
Add( Object^ object) Adds a new row and populates the cells in the row with the
objects specified by the arguments
All versions of the Add()function return a value of type intthat is the index of the last row that wasadded to the collection If the DataSourceproperty for the DataGridViewcontrol is not null, or thecontrol has no columns, all versions of the Add()function throw an exception of type
You will first try unbound mode in a working example where you set up the DataGridViewcontrol bysetting its properties from the Form Design tab
Trang 38Accessing Data Sources in a Windows Forms Application
Try It Out The DataGridView Control in Unbound Mode
This example displays a list of books where each book is specified by the ISBN, the title, the author, andthe publisher Create a new Windows Form project with the name Ex22_01 Add a DataGridViewcon-trol to the form and click the arrow at top-right of the control to displays the pop-up menu show inFigure 22-2
Figure 22-2
If you click the bottom menu item, Dock in parent container, the control fills the client area of theform The top menu item is for selecting a data source, but you are not going to specify a data source thistime If you click the AddColumnmenu item, the dialog box shown in Figure 22-3 for entering columns
to the control is displayed
The Unbound columnradio button is checked because there is no data source identified for the control,which is the way it should be for this example The Name:entry is the value of the Nameproperty for thecolumn, and the Header Text:entry is the value of the HeaderTextproperty, which corresponds to thetext that is shown in the control as the column heading If you extend the list box that is for selecting theType:value, you’ll see you have a range of choices for the type of column Here you should leave it as thedefault choice — a TextBoxcolumn — because you’ll be adding strings as the data to be displayed Theother column types in the list deal with provide for various controls in the cells representing the data:
DataGridViewButtonColumn This type is used to display a button in each cell in the
column
DataGridViewCheckBoxColumn This type is used when you want to store boolvalues
(System::Booleanobjects) orSystem::Windows::Forms::CheckStateobjects as check-boxes in the cells in the column
Table continued on following page
Trang 39Chapter 22
DataGridViewComboBoxColumn This type is used when you want to display a drop-down list
in each cell in the column
DataGridViewImageColumn You select this type when each cell in the column is to
dis-play and image in each cell in the column
DataGridViewLinkColumn You use this type when each cell in the column is to display
a link
To add a column to the control, you enter values for the Name:and Header Text:, select the Type:from the list if you want other than the default type, and click the Addbutton In this example, you canadd columns with the names ISBN, Title, Author, and Publisher The Header Text:entry can bethe same as the name in each case When you have entered the columns, click the Closebutton to closethe dialog (the Close button is visible after you have added at least one column)
You can edit the columns at any time by clicking the Edit Columnsitem in the pop-up menu; it plays the dialog box shown in Figure 22-4
dis-Figure 22-3
The Edit Columnsdialog box enables you to resequence existing columns, to add new columns, or todelete columns; you can also edit any of the properties for a column
Trang 40Figure 22-4
Return to the Design tab and change the Textproperty for the form to My Book List If you displaythe code for the form, you can modify the constructor to add data to the DataGridViewcontrol usingthe Add()function for the Rowsproperty, like this:
Form1(void){
InitializeComponent();
//
//TODO: Add the constructor code here// Create book data, one book per arrayarray<String^>^ book1 = {L”0-09-174271-4”, L”Wonderful Life”,
L”Stephen Jay Gould”, L”Hutchinson Radius”};
array<String^>^ book2 = {L”0-09-977170-5”, L”The Emperor’s New Mind”,
L”Roger Penrose”, L”Vintage”};
array<String^>^ book3 = {L”0-14-017996-8”,L”Metamagical Themas”,
L”Douglas R Hofstadter”, L”Penguin”};
array<String^>^ book4 = {L”0-201-36080-2”, L”The Meaning Of It All”,
L”Richard P Feynman”, L”Addison-Wesley”};
array<String^>^ book5 = {L”0-593-03449-X”, L”The Walpole Orange”,
L”Frank Muir”, L”Bantam Press”};
array<String^>^ book6 = {L”0-439-99358-X”, L”The Amber Spyglass”,
L”Philip Pullman”, L”Scholastic Children’s Books”};array<String^>^ book7 = {L”0-552-13461-9”, L”Pyramids”,
L”Terry Pratchett”, L”Corgi Books”};
array<String^>^ book8 = {L”0-7493-9739-X”, L”Made In America”,
L”Bill Bryson”, L”Minerva”};
// Create Array of booksarray<array<String^>^>^ books ={book1, book2, book3, book4,
book5, book6, book7, book8};
1097
Accessing Data Sources in a Windows Forms Application