Extending Windows Controls

The Windows Forms controls presented in Chapter 4, "Building Simple User Interfaces with Windows Forms," can be used to create rich and varied user interfaces. Think of these controls as the starting point for your applications. You can build all your forms from the WinForms controls alone, but you are by no means restricted to them.

You can create a single control of your own that inherits from one of the WinForms controls, and extends the library functionality. Alternatively, you can create your own controls by pulling together two or more WinForms controls into a WinForms user control. This collection of controls and code can be placed onto any other form, and works as a self-contained piece of the user interface.

To build this chapter's samples, start by creating a WinForms application called AdvancedUI . In a separate instance of Visual Studio.NET, create a Windows control library project called control . These two solutions will form the basis of the examples shown throughout this chapter.

Extending a Library Control

A class that inherits from a WinForms control need only provide the extra functionality you choose to add; the basic functionality is all inherited from the base class with no coding required by you. To demonstrate how simple it can be, this section shows how to build a text box that accepts only numbers . There are two ways to achieve this: one is to react to each keystroke and discard anything that's not a number, and the other is to wait until the control loses focus and then validate the text it holds. The second approach validates entries that are pasted in as well as those that are typed, so it's a better approach.

Start by adding a generic C++ class called NumbersOnlyTextBox to the control project, and edit NumbersOnlyTextBox.h so it looks like this:

 
 using namespace System; namespace control {    public __gc class NumbersOnlyTextBox :       public System::Windows::Forms::TextBox    {    }; } 

Leave NumbersOnlyTextBox.cpp as a stub file that just brings in the header file. You can't just delete it, or else the class won't be compiled as part of compiling the project. It should look like this:

 
 #include "StdAfx.h"  #include ".\numbersonlytextbox.h"  #using <mscorlib.dll> 

Because this text box inherits from System::Windows::Forms::TextBox , it has all the properties and methods of that class. There are no mandatory steps involved in inheriting from text box, but in order to give this text box different behavior than the library TextBox class, you have to write some code that implements that behavior.

Whenever a form is submitted or accepted, a Validating event is raised. All controls can override the OnValidating() method to validate themselves . In addition, many controls have a Valid property ( get_Valid() in C++ syntax) that can be used to determine whether a control is valid. Because this control will have this property, you can use it in the override of OnValidating() . Add the entire method inside the class definition:

 
 protected:     void OnValidating(System::ComponentModel::CancelEventArgs* e)     {         if (!Valid)             e->Cancel = true;     System::Windows::Forms::TextBox::OnValidating(e);     return;     } 

When the content of the text box is not valid, this code cancels the event that triggered the validation, which for a text box is usually switching focus to another control or clicking a button that causes validation.

The next step is to write the Valid property. It can rely on a helper method called IsValid() . Add this code inside the class definition:

 
 public:     [System::ComponentModel::Browsable(false)]      __property bool get_Valid()     {         return IsValid(Text);     } 

The attribute on this property keeps it from appearing in the Properties window for the control when it's used in the designer. By default, the properties you write are added to the Properties window. This is great for design-time properties, but not helpful for a runtime read-only property like Valid .

The last step in creating this control is to write IsValid() , which works with any string rather than only with the text property of the control. Add this method inside the class definition:

 
 private:    bool IsValid(String* Contents)    {       Regex* re = new Regex("\d*");       Match* m;       m = re->Match(Contents);       bool test = m->Success             && m->Index == 0             && m->Length == Contents->Length;       if (!test)          System::Windows::Forms::MessageBox::Show("enter only numbers here");       return test;    } 

For this code to compile, you need to add this line at the top of the file:

 
 using namespace System::Text::RegularExpressions; 

The IsValid() method relies on the regular expressions support in the .NET Framework to confirm that the string consists entirely of digits (09). You could construct a more complex regular expression that could handle characters such as and .; this expression will match positive integers of any length. It ensures that in addition to finding the pattern ( \d represents a digit and * means any number of them) the pattern begins at the start of the string and lasts until the end of the string. This ensures that strings like 1a and b2 will rejected even though they contain digits.

To use this control, first build it and correct any errors. Then switch to the instance of Visual Studio in which AdvancedUI , the WinForms project, is open . Add a reference to the controls assembly (you have to browse from the .NET tab on the Add Reference dialog box).

Use the toolbox to drag two text boxes onto the form. Right-click on the surface of the form and choose View Code to open form1.h. About half way down the file, find this line:

 
 private: System::Windows::Forms::TextBox *  textBox1; 

Change it to read like this:

 
 private: control::NumbersOnlyTextBox *  textBox1; 

Expand the InitializeComponent() method, and find this line:

 
 this->textBox1 = new System::Windows::Forms::TextBox(); 

Change it to read like this:

 
 this->textBox1 = new control::NumbersOnlyTextBox(); 

Build the project and switch back to the designer; you should still see two text boxes on the form. If there is a problem with the changes you made to the code, the upper text box will disappear. When you correct the code, you will again see the text box in the designer. Run the project, and try using Tab to move to the second text box while the first still holds the default text, textBox1 . You should get an error message from the control itself. Confirm that the second text box allows you to type whatever you want and does not produce error messages.

Creating a User Control

When you created the control project, a user control was generated for you with the rather unfortunate name of controlControl . It's quite difficult to change the name, and simpler just to remove it and add a new one with a better name. Remove controlcontrol.h and controlcontrol.cpp from the project, and then right-click the project in Solution Explorer and choose Add, Add New Item. Select Windows User Control. Name the control PhoneNumber .

A blank design surface appears, much like the empty form in a Windows Forms project. Drag three text boxes onto the surface, set the text property of each to an empty string, and then resize the text boxes and the surface until your control resembles Figure 15.1.

Figure 15.1. Placing three text boxes on the user control.

graphics/15fig01.gif

Open the code for form1.h and change the declaration and the creation of each text box to be a control::NumbersOnlyTextBox , as in the previous section. Even though this user control is in the same namespace as the text box class you've defined, the designer doesn't recognize the class unless you provide the namespace explicitly. The declarations should end up reading like this:

 
 private: control::NumbersOnlyTextBox *  textBox1; private: control::NumbersOnlyTextBox *  textBox2; private: control::NumbersOnlyTextBox *  textBox3; 

The InitializeComponent() method should start like this:

 
 void InitializeComponent(void) {    this->textBox1 = new control::NumbersOnlyTextBox();    this->textBox2 = new control::NumbersOnlyTextBox();    this->textBox3 = new control::NumbersOnlyTextBox();    this->SuspendLayout(); 

At this point you can use this user control on a form, but it's going to be more useful if it does more than just hold the three text boxes. For example, why not expose a Text property that combines the three text boxes, and saves a little bit of coding for all the applications that use the PhoneNumber control? Add this method to the class definition:

 
 public: __property String* get_Text() {     System::Text::StringBuilder* sb = new System::Text::StringBuilder();     if (String::Compare(textBox1->Text,"" ) != 0)     {         sb->Append("(");         sb->Append(textBox1->Text);         sb->Append(")");     }     sb->Append(textBox2->Text);     sb->Append("");     sb->Append(textBox3->Text);     return sb->ToString(); } 

Build the project to make sure there are no errors, and then switch back to the AdvancedUI project.

You could add a PhoneNumber control to the form by dragging some other control on and then editing the behind-the-scenes code, as you did in the previous section for the NumbersOnlyTextBox , but there's no obvious choice for a control that's close to a PhoneNumber control. At this point it's best to add the control to the toolbox so you can drag it on to the form like any other control. Here's how to do so:

  1. Open Form1.h in the designer.

  2. Select the My User Controls tab.

  3. Right-click the empty part of the toolbox, and choose Add/Remove Items.

  4. On the Customize Toolbox dialog box, select the .NET Framework Components tab and click Browse.

  5. Browse to control.dll in the control folder and click Open.

  6. Scroll through the list of controls and make sure NumbersOnlyTextBox in the control namespace and PhoneNumber in the control namespace both have a selected check box on the left.

  7. Click OK to add these controls to the tab.

Once the PhoneNumber control is on the tab, you can drag one onto the form like any other control. (You can try a NumbersOnlyTextBox , too, if you like.)

Drag on a PhoneNumber and a button. Change the name and the text of the button to Phone , and double-click it to add a click handler. Add this code to that handler:

 
 private: System::Void Phone_Click(System::Object *  sender,                                   System::EventArgs *  e) {     String* msg = S" Phone control text: ";     msg = msg->Concat(phoneNumber1->Text);     MessageBox::Show(msg); } 

Build the application and run it. Notice which text boxes allow you to enter arbitrary text and which demand numbers. Click the Phone button to see the Text property of the control in use, as shown in Figure 15.2.

Figure 15.2. The text property of the user control simplifies coding for the forms that use the control.

graphics/15fig02.gif

That's how simple it is to add your own controls to an application, whether they are an extension of a library control with inheritance, or an aggregation of several controls, with useful properties of the combined controls layered onto the aggregation.



Microsoft Visual C++. NET 2003 Kick Start
Microsoft Visual C++ .NET 2003 Kick Start
ISBN: 0672326000
EAN: 2147483647
Year: 2002
Pages: 141
Authors: Kate Gregory

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net