Managed C++ and .NET Development: Visual Studio .NET 2003 Edition
First things first: Dialog boxes are just Forms that are called or started differently and can, if you want, pass and/or return data and return a DialogResult. That's it! Forget what you once knew about dialog boxes (if you were a classic Visual C++ MFC programmer)—things have gotten a lot easier.
Everything that you've learned so far in this chapter works the same for dialog boxes. All you need to do is learn a couple of optional features and how to call the dialog box itself, and then you'll know all you need to develop dialog boxes.
Custom Dialog Boxes
Building a custom dialog box is almost exactly the same as creating the main Win Form, except it requires two additional steps. Here are the steps you follow to create a custom dialog box:
-
Right-click the project folder within Solution Explorer.
-
Select Add New Item from the drop-down menu item Add. A dialog box similar to the one in Figure 10-16 appears.
Figure 10-16: The Add New Item dialog box -
Select the Windows Form (.NET) icon from the Templates panel and give the dialog box a name. I used MyDialog.
-
Click Open. This will provide you with an empty form in the design window.
-
Build the form exactly as you do the main form.
You can now work with this form in exactly the same way as you do with the application's main form, except for a couple of minor things.
The first minor difference is that if you want to pass information to the dialog box or get information back from the dialog box, you need to add properties to your form to get and set the information:
public: __property void set_PassedValue(String *value) // PassedValue property { tbPassedValue->Text = value; } __property String *get_PassedValue() { return tbPassedValue->Text; }
Another method of doing this would be to change the constructor to send data to the dialog box, but I prefer properties. Plus, if you use the constructor to pass data to the dialog box, you still need to create properties or methods to send data back, so why not bite the bullet and use properties in both cases? This method is clean and safe (because you can verify the validity of the passed data) and it's easy to use.
The second change that you can make, which is totally optional, is to change the style of the dialog box to look more like a dialog box and less like a form:
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedToolWindow; // Or this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::SizableToolWindow;
The third difference is that you want to have any buttons that close your dialog box return a DialogResult. The .NET Framework class library provides a number of possible DialogResults (see Table 10-2).
TYPE | DESCRIPTION |
---|---|
Abort | Return the value Abort. Usually you will have a button labeled Abort to handle this. |
Cancel | Return the value Cancel. This is the value returned when the Esc key is pressed (if enabled) or the close dialog box button is clicked. Also, you will have a button on the form labeled Cancel. |
Ignore | Return the value Ignore. Usually you will have a button labeled Ignore to handle this. |
No | Return the value No. Usually you will have a button labeled No to handle this. |
None | Nothing is returned. You will use this with a modal dialog box, which is discussed later. |
OK | Return the value OK. This is the value returned when the Enter key is pressed (if enabled). Also, you will have a button on the form labeled OK. |
Retry | Return the value Retry. Usually you will have a button labeled Retry to handle this. |
Yes | Return the value Yes. Usually you will have a button labeled Yes to handle this. |
To return a DialogResult value to the calling form, you need to assign, to the button that will end the dialog, the desired DialogResult value:
bnOK->DialogResult = DialogResult::OK;
When the button is clicked, it will automatically return the DialogResult it was set to (DialogResult::OK is set in the preceding code). By the way, you can still handle the Click event, if you need to, for the button. (You can even change its DialogResult in the handler if you really want to. For example, you could turn DialogResult::OK into DialogResult::Cancel if no text is entered in the dialog box.)
The final change you are probably going to want to make is to assign default buttons to respond to the Accept and Cancel conditions. You do this by assigning a button to the form's AcceptButton and CancelButton properties:
AcceptButton = bnOK; CancelButton = bnCancel;
Once you have performed the preceding additional steps, you have a complete custom dialog box. Listing 10-14 shows the code of a custom dialog box that takes in some text, places it in a text box, allows it to be updated, and then returns the text back updated to the calling form. The dialog box also allows the user to abort or cancel the dialog box.
Listing 10-14: The MyDialog Class File
#pragma once using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; namespace CustomDialog { public __gc class MyDialog : public System::Windows::Forms::Form { public: MyDialog(void) { InitializeComponent(); } public: __property void set_PassedValue(String *value) // PassedValue property { tbPassedValue->Text = value; } __property String *get_PassedValue() { return tbPassedValue->Text; } protected: void Dispose(Boolean disposing) { if (disposing && components) { components->Dispose(); } __super::Dispose(disposing); } private: System::Windows::Forms::Button * bnOK; private: System::Windows::Forms::Button * bnAbort; private: System::Windows::Forms::Button * bnCancel; private: System::Windows::Forms::TextBox * tbPassedValue; private: System::ComponentModel::Container * components; void InitializeComponent(void) { this->tbPassedValue = new System::Windows::Forms::TextBox(); this->bnOK = new System::Windows::Forms::Button(); this->bnAbort = new System::Windows::Forms::Button(); this->bnCancel = new System::Windows::Forms::Button(); this->SuspendLayout(); // // tbPassedValue // this->tbPassedValue->Location = System::Drawing::Point(15, 25); this->tbPassedValue->Name = S"tbPassedValue"; this->tbPassedValue->Size = System::Drawing::Size(250, 22); this->tbPassedValue->TabIndex = 0; this->tbPassedValue->Text = S""; // // bnOK // this->bnOK->DialogResult = System::Windows::Forms::DialogResult::OK; this->bnOK->Location = System::Drawing::Point(15, 72); this->bnOK->Name = S"bnOK"; this->bnOK->TabIndex = 1; this->bnOK->Text = S"OK"; // // bnAbort // this->bnAbort->DialogResult = System::Windows::Forms::DialogResult::Abort; this->bnAbort->Location = System::Drawing::Point(104, 72); this->bnAbort->Name = S"bnAbort"; this->bnAbort->TabIndex = 2; this->bnAbort->Text = S"Abort"; // // bnCancel // this->bnCancel->DialogResult = System::Windows::Forms::DialogResult::Cancel; this->bnCancel->Location = System::Drawing::Point(192, 72); this->bnCancel->Name = S"bnCancel"; this->bnCancel->TabIndex = 3; this->bnCancel->Text = S"Cancel"; // // MyDialog // this->AcceptButton = this->bnOK; this->AutoScaleBaseSize = System::Drawing::size(6, 15); this->CancelButton = this->bnCancel; this->ClientSize = System::Drawing::Size(300, 120); this->Controls->Add(this->bnCancel); this->Controls->Add(this->bnAbort); this->Controls->Add(this->bnOK); this->Controls->Add(this->tbPassedValue); this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedToolWindow; this->Name = S"MyDialog"; this->Text = S"My Custom Dialog"; this->ResumeLayout(false); } }; }
Figure 10-17 shows what the preceding example looks like when you execute it.
Now let's take a look at the code to implement a custom dialog box (see Listing 10-15). The example calls the dialog box by clicking anywhere in the form.
Listing 10-15: Implementing a Custom Dialog Box
namespace CustomDialog { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; public __gc class Form1 : public System::Windows::Forms::Form { public: Form1(void) //... protected: void Dispose(Boolean disposing) //... private: System::Windows::Forms::Label * lbRetVal; private: System::Windows::Forms::Label * lbRetString; private: System::ComponentModel::Container * components; void InitializeComponent(void) { this->lbRetVal = new System::Windows::Forms::Label(); this->lbRetString = new System::Windows::Forms::Label(); this->SuspendLayout(); // // lbRetVal // this->lbRetVal->Location = System::Drawing::Point(32, 40); this->lbRetVal->Name = S"lbRetVal"; this->lbRetVal->Size = System::Drawing::Size(224, 23); // // lbRetString // this->lbRetString->Location = System::Drawing::Point(32, 88); this->lbRetString->Name = S"lbRetString"; this->lbRetString->Size = System::Drawing::Size(224, 23); // // Form1 // this->AutoScaleBaseSize = System::Drawing::Size(6, 15); this->ClientSize = System::Drawing::Size(292, 270); this->Controls->Add(this->lbRetString); this->Controls->Add(this->lbRetVal); this->Name = S"Form1"; this->Text = S"Click Form to get dialog"; this->Click += new System::EventHandler(this, Form1_Click); this->ResumeLayout(false); } private: System::Void Form1_Click(System::Object * sender, System::EventArgs * e) { MyDialog *mydialog = new MyDialog(); mydialog->PassedValue = S"This has been passed from Form1"; if (mydialog->ShowDialog() == DialogResult::OK) lbRetVal->Text = S"OK"; else if (mydialog->DialogResult == DialogResult::Abort) lbRetVal->Text = S"Abort"; else lbRetVal->Text = S"Cancel"; lbRetString->Text = mydialog->PassedValue; } }; }
Figure 10-18 shows what the preceding example looks like when you execute it.
Not much of a change, is there? First, you create an instance of the dialog box:
MyDialog *mydialog = new MyDialog();
Optionally, you can pass all the data you want to the dialog box:
mydialog->PassedValue = S"This has been passed from Form1";
Then you call the dialog box in one of two ways:
-
ShowDialog()
-
Show()
The first mode, ShowDialog(), is modal. In this mode, you wait for the dialog box to finish before you continue processing. Normally, you would check the DialogResult upon exit, as you do in the example, but that is not necessary:
if (mydialog->ShowDialog() == DialogResult::OK) lbRetVal->Text = S"OK"; else if (mydialog->DialogResult == DialogResult::Abort) lbRetVal->Text = S"Abort"; else lbRetVal->Text = S"Cancel";
The second mode, Show(), is modeless. In this mode, the dialog box opens and then returns control immediately back to its caller. You now have two threads of execution running. I cover threads in Chapter 16.1 discuss modeless dialog boxes more then, but here is the code to start a modeless dialog box:
mydialog->Show();
The final thing you might do (again, this is optional) is grab the changed data out of the dialog box:
lbRetString->Text = mydialog->PassedValue;
By the way, I have been using Strings to pass data back and forth between the dialog box and the main application. This is not a restriction, though—you can use any data type you want.
Common Dialog Boxes
When you've worked with Windows for any length of time, you soon come to recognize some common dialog boxes that many applications use. The .NET Framework class library provides you easy access to using these same dialog boxes in your programs. Table 10-3 shows a list of the available common dialog boxes.
DIALOG BOX | DESCRIPTION |
---|---|
ColorDialog | A dialog box to select a color |
FontDialog | A dialog box to select a font |
OpenFileDialog | A common Open File dialog box |
PageSetupDialog | A dialog box that manipulates page settings, such as margins |
PrintDialog | A dialog box to select a printer and the portion of the document you want to print |
SaveFileDialog | A common File Save dialog box |
You call the common dialog boxes in the same way you do the custom dialog box you just built. Listing 10-16 shows just how simple it is to call the ColorDialog. Calling all the other custom dialog boxes is done the same way.
Listing 10-16: Calling a Common ColorDialog
namespace ColorDlg { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; public __gc class Form1 : public System::Windows::Forms::Form { public: Form1(void) //... protected: void Dispose(Boolean disposing) //... private: System::ComponentModel::Container * components; void InitializeComponent(void) { this->AutoScaleBaseSize = System::Drawing::Size(6, 15); this->ClientSize = System::Drawing::Size(292, 270); this->Name = S"Form1"; this->Text = S"Common Color Dialog - Click Form"; this->Click += new System::EventHandler(this, Form1_Click); } private: System::Void Form1_Click(System::Object * sender, System::EventArgs * e) { ColorDialog *colordialog = new ColorDialog(); if (colordialog->ShowDialog() == DialogResult::OK) { BackColor = colordialog->Color; } } }; }
There is nothing new or special here. First, check to make sure that the dialog box exited with the DialogResult of OK, and then set the color of the object you want changed with the value in the Color property of the ColorDialog.
Figure 10-19 shows what the example looks like when you execute it.
Категории