Building Controls from Other Controls

227 Dim dlg As New ColorDialog dlg.Color = Label1.BackColor If dlg.ShowDialog = DialogResult.OK Then Label1.BackColor = dlg.Color End If dlg.Dispose End Sub Resent Click event handler Private Sub mnuFormatReset_Click _ ByVal sender As Object, _ ByVal e As EventArgs _ Handles mnuFormatReset.Click Label1.Font = origFont Label1.ForeColor = origForeColor Label1.BackColor = origBackColor End Sub End Class

5.6 Creating a Control

A control is a component with a visual representation. The Windows Forms class library provides the base functionality for controls through the Control class defined in the System.Windows.Forms namespace. All controls derive directly or indirectly from the Control class. In addition, Windows Forms provides a class called UserControl for the purpose of making it easy to write custom control classes. The derivation of the UserControl class is shown in Figur e 5- 27 . Figure 5-27. The derivation hierarchy of the UserControl class

5.6.1 Building Controls from Other Controls

The easiest way to create a new control is to aggregate and modify the functionality of one or more existing controls. To do this in Visual Studio .NETs Windows Forms Designer, perform the following steps: 1. Choose Project Add User Control from the main menu. 2. Type the name of the .vb file that will hold the code for the control, and click OK. The designer displays a blank user control in design mode, as shown in Figur e 5- 28 . Figure 5-28. A blank user control in Visual Studio .NETs Windows Forms Designer 228 3. Add controls from the Toolbox window just as you would when laying out a form. Controls that are made part of another control are called constituent controls . Figur e 5- 29 shows a user control that has two constituent controls: a Label and a TextBox. Figure 5-29. A user control with two constituent controls The user control shown in Figur e 5- 29 is a good start on a captioned text-box control—a text box that carries around its own caption. These additional steps would also be helpful: a. Set the Label controls AutoSize property to True so the label expands to the size needed for displaying its text. b. Dock the Label control to the left side of the user control. This allows the TextBox control to be docked to the Label control. The benefit of docking the TextBox control to the Label control is that the TextBox control will move whenever the Label control resizes itself. c. Dock the TextBox control to fill the remainder of the space. 4. Add any appropriate properties to the user control. This could involve overriding properties inherited from base classes or creating new properties. For the captioned text-box user control, do the following: a. Override the Text property. The purpose of this property is to allow the client environment to set and read the text displayed in the constituent TextBox control. Here is the code: b. Public Overrides Property Text As String c. Get d. Return txtText.Text e. End Get f. SetByVal Value As String g. txtText.Text = Value h. End Set End Property As can be seen from the code, the user controls Text property is simply mapped to the TextBox controls Text property. i. Create a new property for setting the caption text. Here is the code: j. Public Property Caption As String k. Get l. Return lblCaption .Text m. End Get n. SetByVal Value As String o. lblCaption .Text = Value p. End Set 229 End Property In this case, the Caption property is mapped to the Label controls Text property. 5. Add any appropriate events to the user control. This could involve invoking base-class events or creating and invoking new events. For the captioned text-box user control, do the following: a. Add a handler for the constituent TextBox controls TextChanged property. Within the handler, invoke the base classs TextChanged event. Here is the code: b. Private Sub txtText_TextChanged _ c. ByVal sender As Object, _ d. ByVal e As EventArgs _ e. Handles txtText.TextChanged f. Me.OnTextChangede End Sub Notice that this code calls the OnTextChanged method, which is declared in the Control class. The purpose of this method is to fire the TextChanged event, which is also declared in the Control class Visual Basic .NET doesnt provide a way to fire a base-class event directly. There are On EventName methods for each of the events defined in the Control class. The OnTextChanged method is overridable. If you override it in your derived class, be sure that your overriding method calls MyBase.OnTextChanged. If you dont, the TextChanged event wont be fired. g. Declare a new event to notify the client when the user controls Caption property is changed. Name the event CaptionChanged. Add a handler for the Label controls TextChanged event and raise the CaptionChanged event from there. Heres the code: h. Event CaptionChanged As EventHandler i. j. Private Sub lblCaption_TextChanged _ k. ByVal sender As Object, _ l. ByVal e As EventArgs _ m. Handles lblCaption.TextChanged n. RaiseEvent CaptionChangedMe, EventArgs.Empty End Sub Note the arguments to the CaptionChanged event. The value Me is passed as the sender of the event, and EventArgs.Empty is passed as the event arguments. The Empty field of the EventArgs class returns a new, empty EventArgs object. 6. Add any appropriate attributes to the syntax elements of the class. For example, the Caption property will benefit from having a Category attribute and a Description attribute, as shown in bold here:

7. 8.

CategoryAppearance, _ 9. DescriptionThe text appearing next to the textbox. _ 10. Public Property _ 11. Caption As String 12. Get 13. Return lblCaption .Text 14. End Get 15. SetByVal Value As String 16. lblCaption .Text = Value 17. End Set End Property These attributes are compiled into the code and are picked up by the Visual Studio .NET IDE. The Category attribute determines in which category the property will appear in the 230 Properties window. The Description attribute determines the help text that will be displayed in the Properties window when the user clicks on that property. See Component Attributes in Chapt er 4 for a list of attributes defined in the System.ComponentModel namespace. This is all very similar to creating forms. As with forms, custom controls can be defined directly in code. Ex am ple 5- 4 shows a complete class definition for the captioned text-box control, created without the aid of the Windows Forms Designer. It can be compiled from the command line with this command: vbc filename.vb r:System.dll,System.Drawing.dll,System.Windows.Forms.dll t:library Note the t:library switch for creating a .dll rather than an .exe file. Example 5-4. A custom Control class Imports System Imports System.ComponentModel Imports System.Drawing Imports System.Windows.Forms Public Class MyControl Inherits UserControl Private WithEvents lblCaption As Label Private WithEvents txtText As TextBox Event CaptionChanged As EventHandler Public Sub New MyBase.New Instantiate a Label object and set its properties. lblCaption = New Label With lblCaption .AutoSize = True .Dock = DockStyle.Left .Size = New Size53, 13 .TabIndex = 0 .Text = lblCaption End With Instantiate a TextBox object and set its properties. txtText = New TextBox With txtText .Dock = DockStyle.Fill .Location = New Point53, 0 .Size = New Size142, 20 .TabIndex = 1 .Text = txtText End With Add the label and text box to the forms Controls collection. Me.Controls.AddRangeNew Control {txtText, lblCaption} Set the size of the form. Me.Size = New Size195, 19 End Sub Override the Control classs Text property. Map it to the 231 constituent TextBox controls Text property. CategoryAppearance, _ DescriptionThe text contained in the textbox. _ Public Overrides Property Text As String Get Return txtText.Text End Get SetByVal Value As String txtText.Text = Value End Set End Property Add a Caption property. Map it to the constituent Label controls Text property. CategoryAppearance, _ DescriptionThe text appearing next to the textbox. _ Public Property _ Caption As String Get Return lblCaption.Text End Get SetByVal Value As String lblCaption.Text = Value End Set End Property When the constituent TextBox controls TextChanged event is received, fire the user controls TextChanged event. Private Sub txtText_TextChanged _ ByVal sender As Object, _ ByVal e As EventArgs _ Handles txtText.TextChanged Me.OnTextChangede End Sub When the constituent Label controls TextChanged event is received, fire the user controls CaptionChanged event. Private Sub lblCaption_TextChanged _ ByVal sender As Object, _ ByVal e As EventArgs _ Handles lblCaption.TextChanged RaiseEvent CaptionChangedMe, EventArgs.Empty End Sub End Class After compiling the code in Ex am ple 5- 4 , either the custom control can be added to the Visual Studio .NET toolbox and added to forms just like other controls, or it can be referenced and instantiated from an application compiled at the command line. To add the custom control to the Visual Studio .NET toolbox: 1. Deploy the custom controls .dll file into the client applications bin directory. The bin directory is a directory created by Visual Studio .NET when the client application is created. 2. From the Visual Studio .NET menu, select Tools Customize Toolbox. The Customize Toolbox dialog box appears, as shown in Figur e 5- 3 0 . Figure 5-30. The Customize Toolbox dialog box 232 3. Click the .NET Framework Components tab, then click the Browse button. 4. Browse for and select the .dll file compiled from the code in Ex am ple 5- 4 . 5. The controls in the .dll file are added to the Customize Toolbox dialog box, as shown in Figur e 5- 31 . In this case there is only one control in the .dll file—the MyControl control. Figure 5-31. Adding a control to the Customize Toolbox dialog box 6. Ensure that a checkmark appears next to the control name, and click OK. The control should now appear on the General tab of the Toolbox window, as shown in Figur e 5- 32 . Figure 5-32. The Toolbox window, showing the custom control from Ex a m ple 5 - 4 233 After you add the custom control to the Toolbox, the control can be added to a form just like any other control. To use the custom control from a non-Visual Studio .NET application, these steps are required: 1. Deploy the custom controls .dll file into the same directory as the client .vb file. 2. Declare, instantiate, and use the control in client application code, in the same way that has been done throughout this chapter for standard controls. 3. Reference the controls assembly in the compilation command. The code in Ex am ple 5- 5 shows how to use the control. It can be compiled with this command: vbc MyApp.vb r:System.dll,System.Drawing.dll,System.Windows.Forms.dll,MyControl.dll t:winexe Note that the command should be typed on a single line. Example 5-5. Using a custom control Imports System.Drawing Imports System.Windows.Forms Module modMain System.STAThreadAttribute Public Sub Main System.Threading.Thread.CurrentThread.ApartmentState = _ System.Threading.ApartmentState.STA System.Windows.Forms.Application.RunNew Form1 End Sub End Module Public Class Form1 Inherits System.Windows.Forms.Form Private ctrl As New MyControl Public Sub New ctrl.Caption = This is the caption. ctrl.Text = This is the text. Controls.Addctrl End Sub End Class The resulting display is shown in Figur e 5- 33 . 234 Figure 5-33. Using a custom control

5.6.2 Building Controls That Draw Themselves