You can add some new data members to the EuroLimitsDialogclass to hold theuser limit values first: Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com... You can def
Trang 1::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;
}else{upperLimit = upper;
lowerLimit = lower;
}}
Because the third argument to the Show()function is MessageBoxButtons::OKCancel, the messagebox now has two buttons as shown in Figure 21-23
Figure 21-23
In the Clickevent handler for the OKbutton in the limits dialog box you store the return value from theShow()function in result The type for resulthas to be specified using the scope resolution operator.Otherwise, it is interpreted by the compiler as the DialogResultproperty for the lottoLimitsDialogobject, and the code does not compile If resultcontains the value ::DialogResult::OK, you set theDialogResultproperty for the lottoLimitsDialogobject to ::DialogResult::None, which preventsthe dialog box from closing and allows the limit to be changed Otherwise you set the DialogResultproperty for the dialog to ::Dialog::Cancel, which has the same effect as clicking the Cancelbuttonfor the dialog box so it closes
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 2Handler the Reset Menu Item Event
You 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;
This just resets the limits in the fields in the Form1object and then updates the properties in the dialogobject accordingly You still have to add code to this function to deal with resetting the dialog box towhich 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 applicationwindow is shown in Figure 21-24
Figure 21-24Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 3As 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
Trang 4property 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
in Figure 21-26
Figure 21-26Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 5Select 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:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 6int 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());}
}Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 7The 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 limitsint 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”,Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 8if(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;
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 limits
You 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 Controls
When 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:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 9// Disables controls for selecting upper limitsvoid SetLowerEnabled(void)
{upperValuesLimits->Enabled = false;
Updating 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) {
::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){
Trang 10euroStarsUserMinimum = 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){
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 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”,
MessageBoxButtons::OK, MessageBoxIcon::Exclamation);
}Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 11When 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 valuesarray<int>^ values = gcnew array<int>(buttons->Length);
// Get values from buttons and find index for buttonfor(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 valueSimpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 12if(button == buttons[i]) index = i;
values[index] = newValue; // Store the new value at index
Array::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);
}Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 13The 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:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 14System::Void euroValue_Click(System::Object^ sender, System::EventArgs^ e) {
Button^ button = safe_cast<Button^>(sender);
array<Button^>^ buttons = {euroValue1, euroValue2, euroValue3,
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)){
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 15// 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 Choosemenu item is clicked, so the senderparameter for the handler identifies the menu item, not the button You need a handler that responds tothe original click on the button and you can create this by double-clicking buttonContextMenuin theDesign 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 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 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 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 canSimpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 16now 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 Class
The 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:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 17property 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
return;
}
int value = Int32::Parse(textBox->Text); // Get text box valueSimpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 18bool valid = true; // Indicator for valid entry
for 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 ChooseMenu
You 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 wasSimpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 19right-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 boxif(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 shownuserValueDialog->Values = values = GetButtonValues(buttons);
userValueDialog->LowerLimit = lottoUserMinimum;
userValueDialog->UpperLimit = lottoUserMaximum;
userValueDialog->SetLabelText(lottoUserMinimum, lottoUserMaximum);
}// Check if the button is in the euroValues group boxelse if(euroValues->Controls->Contains(contextButton)){
// The button is in the Values group
array<Button^>^ buttons = {euroValue1, euroValue2, euroValue3,
// 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 shownuserValueDialog->Values = values;
userValueDialog->LowerLimit = euroStarsUserMinimum;
userValueDialog->UpperLimit = euroStarsUserMaximum;
userValueDialog->SetLabelText(euroStarsUserMinimum, euroStarsUserMaximum);
}Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 20// 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 valuesfor(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);
return values;
}Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 21Here 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
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 22ExercisesYou 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-Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 23Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 24an 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 Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 25A 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:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 26Component 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
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 27The 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
gridCntrl->Rows[2]
refers to asingle row
Trang 28unbound 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 columns
You can optionally label the columns in the control by specifying headers to identify the data in each 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:
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 29Add() Adds one or more rows to the collection
Insert() Inserts one or more rows in the collection
AddCopy() Adds a copy of the row specified by the argument
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
L”1246 First Street, AnyTown”);
Each of these statements adds a new row to the collection and the three arguments to the Add()functioncorrespond to the three columns in the control The control must have sufficient columns to accommo-date the number of items you add in a row If you attempt to add more data items to a row than there arecolumns in the control, the excess items are ignored
You will first try unbound mode in a working example where you set up the DataGridViewcontrol bysetting its properties from the Form Design tab
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 30Try It Out The DataGridView Control in Unbound ModeThis 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
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 31DataGridViewComboBoxColumn 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
Trang 32Figure 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};
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 33// Add all the books to the controlfor each(array<String^>^ book in books )dataGridView1->Rows->Add(book);
//
}
There’s an array of type array<String^>created for each book, and the handle to each array is stored
in a variable of type array<array<String^>^>^ Each of the arrays of strings has four elements thatcontain data items that correspond to the columns in the DataGridViewcontrol, so each array defines abook
For convenience, you assemble the handles to the arrays of strings into the booksarray The type forbookslooks a little messy because of the ^repetitions, but it is simply a handle to an array of elementswhere each element is of type array<String^>^ The booksarray enables you to set up all the data inthe control in a single for eachloop The Rowsproperty for the DataGridViewobject returns a handle
of type DataGridViewRowCollection^that references the collection of rows in the control Calling theAdd()function for the object returned by the Rowsproperty adds a complete row to the collection Eachelement in the array that is passed as the argument corresponds to a column in the control, and the type
of data element must correspond with the type you selected for the column Here all the columns havethe same type, so all the cells in a row can be passed in an array If the columns were of different types,you could specify the item for each column by a separate argument to the Add()function, or you coulduse an array of elements of type Object^
If you compile and execute the example by pressing Ctrl+F5, you should see the application windowshown in Figure 22-5
Figure 22-5
You get the scrollbars automatically because the DataGridViewcontrol extends beyond the boundaries
of the client area of the form If you resize the window, eventually the scrollbars disappear
It would be nice if the columns were wide enough to accommodate the maximum length of text theycontain Changing the value of the AutoSizeColumnsModeproperty in the Layout group fixes that.Open the Properties window for the DataGridViewcontrol from the Form1[Design] tab in the Editorpane and change the AutoSizeColumnsModeproperty value to AllCells If you recompile the pro-gram and run it again, you’ll see that the application window is now as shown in Figure 22-6
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 34Figure 22-6
Each column has its width set so that it accommodates the longest string that the column contains.Overall, the resultant application window is not too bad, but you can do a lot more programmatically topersonalize how it looks
Customizing a DataGridV iew Control
As I said earlier in the chapter, the appearance of a DataGridViewcontrol is highly customizable Youwill explore aspects of this using the control in unbound mode, but everything you’ll learn in this con-text applies equally well to using the control in bound mode The appearance of each of the cells in aDataGridViewcontrol is determined by an object of type DataGridViewCellStylethat has the fol-lowing properties:
Property Description
BackColor The value is a System::Drawing::Colorobject that determines the
background color of a cell The Colorclass defines a range of standardcolors as static members The default value is Color::Empty
ForeColor The value is a Colorobject that determines the foreground color of a
cell The default value is Color::Empty.SelectionBackColor The value is a Colorobject that determines the background color of a
cell when it is selected The default value is Color::Empty.SelectionForeColor The value is a Colorobject that determines the foreground color of a
cell when it is selected The default value is Color::Empty.Font The value is a System::Drawing::Fontobject that determines the
font to be used to display text in the cell The default value is null
Table continued on following page
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 35Property Description
Alignment The value determines the alignment of the contents of the cell The
val-ues are defined by the DataGridViewAlignmentenumeration so thevalue can be any of the following constants:
BottomCenter, BottomLeft, BottomRight,MiddleCenter, MiddleLeft, MiddleRight,TopCenter, TopLeft, TopRight, NotSetThe default value is NotSet
WrapMode The value determines whether the text in the cell is wrapped when it is
too long to fit in the cell The value is one of the constants defined bythe DataGridViewTriStateenumeration and can be:
True, False, NotSetThe default value is NotSet.Padding The value is an object of type System::Windows::Forms::Padding
that determines the space between the cell contents and the edge of thecell The Paddingclass constructor requires an argument of type intthat is the padding measured in pixels The default corresponds to nopadding in the cell
Format The value is a format string that determines how the content of the
string is formatted This is the same kind of formatting as you havebeen using in the Console::WriteLine()function The default value
is an empty string
This is not an exhaustive list of the properties of a DataGridViewCellStyleobject, just those that relate
to the appearance of a cell
The way in which the appearance of a particular cell is determined is quite complicated because youhave a number of different properties that you can set in a DataGridViewcontrol that all determinehow a given cell or group of cells is displayed, and several of these can be in effect at any given time Forexample, you can set property values that specify the appearance of a row of cells, or a column of cells,
or all the cells in the control, and these can all be in effect concurrently Clearly, because a row and umn always intersect, all three of these possibilities apply to any given cell, so you have an apparentconflict
col-Each cell in a DataGridViewcontrol is represented by a System::Windows::Forms::DataGridViewCellobject, and the appearance of any given cell, including header cells, is determined by the value of itsInheritedStyleproperty The value of the InheritedStyleproperty for a cell is arrived at by look-ing at all the possible properties that return a value that is a DataGridViewCellStyleobject thatapplies to the cell and then considering these properties in a priority sequence; the first property in thesequence that is found to be set is the one that takes effect The determination of the value of the
InheritedStyleproperty for header cells for rows and columns is handled differently from theInheritedStyleproperty for other cells, so I’ll discuss them separately, starting with header cells.Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 36Customizing Header Cells
The InheritedStyleproperty value for each header cell in the control is determined by consideringthe values of following properties in sequence:
❑ The Styleproperty for the DataGridViewCellobject that represents the cell
❑ The ColumnHeadersDefaultCellStyleproperty or the RowHeadersDefaultCellStyleproperty for the control object
❑ The DefaultCellStyleproperty for the control object
So if the Styleproperty value for the cell object has been set, the InheritedStyleproperty for the celltakes this value and determines the appearance of the cell If not, the next candidate takes effect if thevalue for that has been set If the second choice has not been set, the DefaultCellStyleproperty forthe control is applied
Don’t forget that the value of the InheritedStyleproperty is an object of type DataGridViewCellStyle,which itself has properties that determine various aspects of the appearance of the cell The process ofgoing through the priority sequence applies to each of the properties of the DataGridViewCellStyleobject, so overall there may be contributions from more than one of the properties in the prioritysequence
Customizing Non-Header Cells
The InheritedStyleproperty value for each non-header cell in the control (the non-header cells beingthe cells containing data) is determined from the following properties in the DataGridViewobject insequence:
❑ The Styleproperty for the DataGridViewCellobject that represents the cell
❑ The DefaultCellStyleproperty for the DataGridViewRowobject that represents the row taining the cell You would typically reference the DataGridViewRowobject by indexing theRowsproperty for the control object
con-❑ The AlternatingRowsDefaultCellStyleproperty for the control object; this applies only tocells in rows with odd index numbers
❑ The RowsDefaultCellStyleproperty for the control object
❑ The DefaultCellStyleproperty for the DataGridViewColumnobject that contains the cell.You would typically access a DataGridViewColumnobject by indexing the Columnspropertyfor the control object
❑ The DefaultCellStyleproperty for the control object
Potentially you could have a different DataGridViewCellStyleobject for each cell, but for efficiencyyou need to keep the number of such objects to a minimum
The next Try It Out explores some of these possibilities in an example where you set up theDataGridViewobject yourself
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 37Try It Out Setting the Appearance of the Control
Create a new CLR project using the Windows Forms template with the name Ex22_02 Add a
DataGridViewcontrol to the form in the Design tab and change its (Name)property to dataGridView.This is the name of the handle in the class that references the control object You can also change theTextproperty for the form to “My Other Book List.”For the rest of the example, you are going to
be working with the code in the constructor
The data that you display is similar to that in the previous example, but to extend the possibilities a tle, you’ll add a date entry at the beginning of each row specifying a book, so the cells in the first columnwill contain references to objects of type System::DateTime, and the remaining columns will bestrings The DateTimeclass defines an instant in time that you typically specify as a date plus the time
lit-of day In the example, only the date is lit-of interest, so you’ll use a constructor that accepts only threearguments: the year, the month, and the day
Setting Up the Data
The first step is to create the data to be displayed Add the following code to the Form1constructor, afterthe call to InitializeComponent():
// Create book data, one book per array
array<Object^>^ book1 = {gcnew DateTime(1999,11,5), L”0-09-174271-4”,
L”Wonderful Life”, L”Stephen Jay Gould”, L”Hutchinson Radius”};array<Object^>^ book2 = {gcnew DateTime(2001,10,25), L”0-09-977170-5”,
L”The Emperor’s New Mind”, L”Roger Penrose”, L”Vintage”};array<Object^>^ book3 = {gcnew DateTime(1993,1,15), L”0-14-017996-8”,
L”Metamagical Themas”, “Douglas R Hofstadter”, L”Penguin”};array<Object^>^ book4 = {gcnew DateTime(1994,2,7), L”0-201-36080-2”,
L”The Meaning Of It All”, L”Richard P Feynman”, L”Addison-Wesley”};array<Object^>^ book5 = {gcnew DateTime(1995,11,6), L”0-593-03449-X”,
L”The Walpole Orange”, “Frank Muir”, L”Bantam Press”};array<Object^>^ book6 = {gcnew DateTime(2004,7,16), L”0-439-99358-X”,
L”The Amber Spyglass”, L”Philip Pullman”, L”Scholastic Children’s Books”};array<Object^>^ book7 = {gcnew DateTime(2002,9,18), L”0-552-13461-9”,
L”Pyramids”, L”Terry Pratchett”, L”Corgi Books”};array<Object^>^ book8 = {gcnew DateTime(1998,2,27), L”0-7493-9739-X”,
L”Made In America”, L”Bill Bryson”, L”Minerva”};
// Create Array of books
array<array<Object^>^>^ books = {book1, book2, book3, book4,
book5, book6, book7, book8};
The basic mechanics of this is the same as in the previous example The differences here are due to eachbook having an extra item of type DateTimein the specification, so the array elements are of typeObject^ You’ll recall that the Objectclass is a base class for every C++/CLI class so you can store ahandle to an object of any class type in an element of type Object^
You can add the following statement to the constructor next:
array<String^>^ headers = {L”Date”, L”ISBN”, L”Title”, L”Author”, L”Publisher”};Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 38This creates an array containing the text that is to appear as column headers in the control You can addthese headers to the control by adding the following code to the constructor:
dataGridView->ColumnCount = headers->Length; // Set number of columnsfor(int i = 0 ; i<headers->Length ; i++)
dataGridView->Columns[i]->Name = headers[i];
The first statement specifies the number of columns in the control by setting the value of theColumnCountproperty; this also establishes the control in unbound mode The forloop sets the Nameproperty for each column object to the corresponding string in the headersarray The Columnsprop-erty for the control returns a reference to the collection of columns, and you just index this to reference aparticular column
You can add the rows to the control in another loop:
for each(array<Object^>^ book in books)dataGridView->Rows->Add(book);
The for eachloop selects each of the elements from the books array in turn add passes it to the Add()method for the reference returned by the Rowsproperty for the control Each element in the books array
is an array of strings, and there are as many strings in the array as there are columns in the control The control has now been loaded with the data, so the number of rows and columns is determined andthe contents of the column headers have been specified You can now set about customizing the appear-ance of the control
Setting Up the Control
You want the control to be docked in the client area of the form, and you can do this by setting the value
of the Dockproperty:
dataGridView->Dock = DockStyle::Fill;
The Dockproperty must be set to one of the constants defined by the DockStyleenumeration; otherpossible values are Top, Bottom, Left, Right, or None, and these specify the sides of the control that aredocked
You can also relate the position of the control to the client area of the form by setting the Anchorerty for the control.The value of Anchorproperty specifies the edges of the control that are to beattached to the client area of the form The value is a bitwise combination of the constants defined by the AnchorStylesenumeration and can be any or all of Top, Bottom, Left, and Right For example toanchor the top and left sides of the control, you would specify the value as AnchorStyles::Top &AnchorStyles::Left Setting the Anchorproperty fixes the position of the control plus its scrollbarswithin the container at a given size, so when you resize the application, window the control and itsscrollbars remain at a fixed size If you set the Dockproperty as in the previous statement, resizing theapplication window exposes more or less of the control and the scrollbars adjust accordingly, so that ismuch better in this case
prop-You want the width of the columns to be adjusted to accommodate the data in the cells, and you can putthis into effect by calling the AutoResizeColumns()function:
dataGridView->AutoResizeColumns();
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 39This statement adjusts the width of all columns to accommodate the current contents, and this includesheader cells Note that this is effective at the time the function is called, so the contents need to be therewhen you call it If the contents are changed subsequently, the column width is not adjusted If you wantthe column widths to be adjusted whenever the contents of the cells change, you should also set theAutoSizeColumnsModeproperty for the control, like this:
dataGridView->AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode::AllCells;
The value must be one of the constants defined by the DataGridViewAutoSizeColumnsModetion, and the other possible values are ColumnHeader, AllCellsExceptHeader, DisplayedCells,DisplayedCellsExceptHeader, Fill, and None Of course, these are also the property values in thelist for this property that displays on the Properties page for the DataGridViewcontrol
enumera-It may be that you want to allow only the columns width for specific columns to be automaticallyadjusted when the contents change; in this case, you set a value for the AutoSizeModeproperty for thecolumn object
There are two further overloaded versions of the AutoResizeColumns()function One accepts an ment of type DataGridViewAutoSizeColumnsMode,and the cells affected are determined by the value
argu-of the argument The other overload is protected and, therefore, for use in functions in a derived class; itaccepts an additional argument of type boolthat indicates whether the cell height is to be considered incalculating a new width
You can set the default background color of all cells in the control like this:
dataGridView->DefaultCellStyle->BackColor = Color::Pink;
This sets the background color as the standard color Pinkthat is defined as a static member of theColorclass The properties of the DefaultCellStyleproperty for the control object only determinewhat applies to a cell in the absence of any higher priority cell style being in effect
You could also set the default foreground color for all cells:
That’s enough for the control for now You’ll personalize the column headers next
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 40Setting Up the Column Headers
If you want to determine the appearance of the column headers yourself, you need to set the value of theEnableHeadersVisualStylesproperty for the control to false:
dataGridView->EnableHeadersVisualStyles = false;
The controls in a WindowsForms application are usually drawn according to the visual styles theme that
is in effect, and this theme determines the appearance of the controls If you are running the applicationunder Windows XP, the controls are drawn according to the Windows XP theme When the
EnableHeadersVisualStylesproperty value is true, the visual styles for the column headers will beset according to the visual styles theme in effect for the application, and the styles you set are ignored.You’ll be setting several properties for the appearance of the headers and an easy way to do this is to create a DataGridViewCellStyleobject for which you can set the properties as you want them, andthen make this object the one that determines the styles for the headers You can create the
DataGridViewCellStyleobject like this:
DataGridViewCellStyle^ headerStyle = gcnew DataGridViewCellStyle;
It would be nice to have the header text in a larger font, and you can set the font by setting a value forthe Fontproperty:
headerStyle->Font = gcnew System::Drawing::Font(“Times New Roman”, 12,
FontStyle::Bold);
The header text is now in 12-point bold characters in the Times New Roman font
You can also set the background and foreground colors for the header cells:
headerStyle->BackColor = Color::AliceBlue;
headerStyle->ForeColor = Color::BurlyWood;
The text is now drawn in the color BurlyWoodagainst an AliceBluebackground If you prefer thing different, the Colorclass offers you a lot of choices, and Intellisense should show the list as youcomplete typing the scope resolution operator
some-To set the appearance of the header cells to correspond with the properties that you’ve set for theheaderStyleobject, you need to add the following statement:
dataGridView->ColumnHeadersDefaultCellStyle = headerStyle;
This sets the value of the ColumnHeadersDefaultCellStyleproperty for the control to be theheaderStylehandle This replaces the existing DataGridViewCellStyle object that was in effect forthe headers
There is one other thing you should do in relation to the column headers The larger font requires theheight of the cells to be adjusted to accommodate it Calling the AutoResizeColumnHeadersHeight()function for the control adjusts the heights of the header cells to accommodate their current contents:dataGridView->AutoResizeColumnHeadersHeight();
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com