1. Trang chủ
  2. » Công Nghệ Thông Tin

Beginning Visual C plus plus phần 10 ppt

119 205 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 119
Dung lượng 2,74 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 2

Handler 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 3

As 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 4

property 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 5

Select 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 6

int get() { return lowerValuesLimit; }

void set(int limit){

lowerValuesLimit = limit;

lowerValuesLimits->Value = limit; // Set as selected in NumericUpDown }

}property int UpperValuesLimit{

int get() { return upperValuesLimit; }

void set(int limit){

upperValuesLimit = limit;

upperValuesLimits->Value = limit; // Set as selected in NumericUpDown }

}property int LowerStarsLimit{

int get() { return lowerStarsLimit; }

void set(int limit){

lowerStarsLimit = limit;

lowerStarsLimits->SelectedItem = limit; // Set as selected in ComboBox lowerStarsLimits->SelectedIndex = // Set index for selected item

lowerStarsLimits->FindString(limit.ToString());}

}

property int UpperStarsLimit{

int get() { return upperStarsLimit; }

void set(int limit){

upperStarsLimit = limit;

upperStarsLimits->SelectedItem = limit; // Set as selected in ComboBox upperStarsLimits->SelectedIndex = // Set index for selected item

upperStarsLimits->FindString(limit.ToString());}

}Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 7

The 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 8

if(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 10

euroStarsUserMinimum = 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 11

When the the menu item is clicked, the handler function displays the message box shown in Figure 21-28.

Figure 21-28

Handling a Button Click

Clicking a button should change the value on the button to a new random value Of course, the 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 12

if(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 13

The 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 14

System::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 16

now 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 17

property 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 18

bool 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 19

right-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 21

Here 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 22

ExercisesYou 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 23

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 24

an 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 25

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:

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 26

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

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Trang 27

The 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 28

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 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 29

Add() 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 30

Try 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 31

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

Trang 32

Figure 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 34

Figure 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 35

Property 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 36

Customizing 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 37

Try 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 38

This 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 39

This 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 40

Setting 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

Ngày đăng: 12/08/2014, 10:21

TỪ KHÓA LIÊN QUAN