
- •Using Your Sybex Electronic Book
- •Acknowledgments
- •Contents at a Glance
- •Introduction
- •Who Should Read This Book?
- •How About the Advanced Topics?
- •The Structure of the Book
- •How to Reach the Author
- •The Integrated Development Environment
- •The Start Page
- •Project Types
- •Your First VB Application
- •Making the Application More Robust
- •Making the Application More User-Friendly
- •The IDE Components
- •The IDE Menu
- •The Toolbox Window
- •The Solution Explorer
- •The Properties Window
- •The Output Window
- •The Command Window
- •The Task List Window
- •Environment Options
- •A Few Common Properties
- •A Few Common Events
- •A Few Common Methods
- •Building a Console Application
- •Summary
- •Building a Loan Calculator
- •How the Loan Application Works
- •Designing the User Interface
- •Programming the Loan Application
- •Validating the Data
- •Building a Math Calculator
- •Designing the User Interface
- •Programming the MathCalculator App
- •Adding More Features
- •Exception Handling
- •Taking the LoanCalculator to the Web
- •Working with Multiple Forms
- •Working with Multiple Projects
- •Executable Files
- •Distributing an Application
- •VB.NET at Work: Creating a Windows Installer
- •Finishing the Windows Installer
- •Running the Windows Installer
- •Verifying the Installation
- •Summary
- •Variables
- •Declaring Variables
- •Types of Variables
- •Converting Variable Types
- •User-Defined Data Types
- •Examining Variable Types
- •Why Declare Variables?
- •A Variable’s Scope
- •The Lifetime of a Variable
- •Constants
- •Arrays
- •Declaring Arrays
- •Initializing Arrays
- •Array Limits
- •Multidimensional Arrays
- •Dynamic Arrays
- •Arrays of Arrays
- •Variables as Objects
- •So, What’s an Object?
- •Formatting Numbers
- •Formatting Dates
- •Flow-Control Statements
- •Test Structures
- •Loop Structures
- •Nested Control Structures
- •The Exit Statement
- •Summary
- •Modular Coding
- •Subroutines
- •Functions
- •Arguments
- •Argument-Passing Mechanisms
- •Event-Handler Arguments
- •Passing an Unknown Number of Arguments
- •Named Arguments
- •More Types of Function Return Values
- •Overloading Functions
- •Summary
- •The Appearance of Forms
- •Properties of the Form Control
- •Placing Controls on Forms
- •Setting the TabOrder
- •VB.NET at Work: The Contacts Project
- •Anchoring and Docking
- •Loading and Showing Forms
- •The Startup Form
- •Controlling One Form from within Another
- •Forms vs. Dialog Boxes
- •VB.NET at Work: The MultipleForms Project
- •Designing Menus
- •The Menu Editor
- •Manipulating Menus at Runtime
- •Building Dynamic Forms at Runtime
- •The Form.Controls Collection
- •VB.NET at Work: The DynamicForm Project
- •Creating Event Handlers at Runtime
- •Summary
- •The TextBox Control
- •Basic Properties
- •Text-Manipulation Properties
- •Text-Selection Properties
- •Text-Selection Methods
- •Undoing Edits
- •VB.NET at Work: The TextPad Project
- •Capturing Keystrokes
- •The ListBox, CheckedListBox, and ComboBox Controls
- •Basic Properties
- •The Items Collection
- •VB.NET at Work: The ListDemo Project
- •Searching
- •The ComboBox Control
- •The ScrollBar and TrackBar Controls
- •The ScrollBar Control
- •The TrackBar Control
- •Summary
- •The Common Dialog Controls
- •Using the Common Dialog Controls
- •The Color Dialog Box
- •The Font Dialog Box
- •The Open and Save As Dialog Boxes
- •The Print Dialog Box
- •The RichTextBox Control
- •The RTF Language
- •Methods
- •Advanced Editing Features
- •Cutting and Pasting
- •Searching in a RichTextBox Control
- •Formatting URLs
- •VB.NET at Work: The RTFPad Project
- •Summary
- •What Is a Class?
- •Building the Minimal Class
- •Adding Code to the Minimal Class
- •Property Procedures
- •Customizing Default Members
- •Custom Enumerations
- •Using the SimpleClass in Other Projects
- •Firing Events
- •Shared Properties
- •Parsing a Filename String
- •Reusing the StringTools Class
- •Encapsulation and Abstraction
- •Inheritance
- •Inheriting Existing Classes
- •Polymorphism
- •The Shape Class
- •Object Constructors and Destructors
- •Instance and Shared Methods
- •Who Can Inherit What?
- •Parent Class Keywords
- •Derived Class Keyword
- •Parent Class Member Keywords
- •Derived Class Member Keyword
- •MyBase and MyClass
- •Summary
- •On Designing Windows Controls
- •Enhancing Existing Controls
- •Building the FocusedTextBox Control
- •Building Compound Controls
- •VB.NET at Work: The ColorEdit Control
- •VB.NET at Work: The Label3D Control
- •Raising Events
- •Using the Custom Control in Other Projects
- •VB.NET at Work: The Alarm Control
- •Designing Irregularly Shaped Controls
- •Designing Owner-Drawn Menus
- •Designing Owner-Drawn ListBox Controls
- •Using ActiveX Controls
- •Summary
- •Programming Word
- •Objects That Represent Text
- •The Documents Collection and the Document Object
- •Spell-Checking Documents
- •Programming Excel
- •The Worksheets Collection and the Worksheet Object
- •The Range Object
- •Using Excel as a Math Parser
- •Programming Outlook
- •Retrieving Information
- •Recursive Scanning of the Contacts Folder
- •Summary
- •Advanced Array Topics
- •Sorting Arrays
- •Searching Arrays
- •Other Array Operations
- •Array Limitations
- •The ArrayList Collection
- •Creating an ArrayList
- •Adding and Removing Items
- •The HashTable Collection
- •VB.NET at Work: The WordFrequencies Project
- •The SortedList Class
- •The IEnumerator and IComparer Interfaces
- •Enumerating Collections
- •Custom Sorting
- •Custom Sorting of a SortedList
- •The Serialization Class
- •Serializing Individual Objects
- •Serializing a Collection
- •Deserializing Objects
- •Summary
- •Handling Strings and Characters
- •The Char Class
- •The String Class
- •The StringBuilder Class
- •VB.NET at Work: The StringReversal Project
- •VB.NET at Work: The CountWords Project
- •Handling Dates
- •The DateTime Class
- •The TimeSpan Class
- •VB.NET at Work: Timing Operations
- •Summary
- •Accessing Folders and Files
- •The Directory Class
- •The File Class
- •The DirectoryInfo Class
- •The FileInfo Class
- •The Path Class
- •VB.NET at Work: The CustomExplorer Project
- •Accessing Files
- •The FileStream Object
- •The StreamWriter Object
- •The StreamReader Object
- •Sending Data to a File
- •The BinaryWriter Object
- •The BinaryReader Object
- •VB.NET at Work: The RecordSave Project
- •The FileSystemWatcher Component
- •Properties
- •Events
- •VB.NET at Work: The FileSystemWatcher Project
- •Summary
- •Displaying Images
- •The Image Object
- •Exchanging Images through the Clipboard
- •Drawing with GDI+
- •The Basic Drawing Objects
- •Drawing Shapes
- •Drawing Methods
- •Gradients
- •Coordinate Transformations
- •Specifying Transformations
- •VB.NET at Work: Plotting Functions
- •Bitmaps
- •Specifying Colors
- •Defining Colors
- •Processing Bitmaps
- •Summary
- •The Printing Objects
- •PrintDocument
- •PrintDialog
- •PageSetupDialog
- •PrintPreviewDialog
- •PrintPreviewControl
- •Printer and Page Properties
- •Page Geometry
- •Printing Examples
- •Printing Tabular Data
- •Printing Plain Text
- •Printing Bitmaps
- •Using the PrintPreviewControl
- •Summary
- •Examining the Advanced Controls
- •How Tree Structures Work
- •The ImageList Control
- •The TreeView Control
- •Adding New Items at Design Time
- •Adding New Items at Runtime
- •Assigning Images to Nodes
- •Scanning the TreeView Control
- •The ListView Control
- •The Columns Collection
- •The ListItem Object
- •The Items Collection
- •The SubItems Collection
- •Summary
- •Types of Errors
- •Design-Time Errors
- •Runtime Errors
- •Logic Errors
- •Exceptions and Structured Exception Handling
- •Studying an Exception
- •Getting a Handle on this Exception
- •Finally (!)
- •Customizing Exception Handling
- •Throwing Your Own Exceptions
- •Debugging
- •Breakpoints
- •Stepping Through
- •The Local and Watch Windows
- •Summary
- •Basic Concepts
- •Recursion in Real Life
- •A Simple Example
- •Recursion by Mistake
- •Scanning Folders Recursively
- •Describing a Recursive Procedure
- •Translating the Description to Code
- •The Stack Mechanism
- •Stack Defined
- •Recursive Programming and the Stack
- •Passing Arguments through the Stack
- •Special Issues in Recursive Programming
- •Knowing When to Use Recursive Programming
- •Summary
- •MDI Applications: The Basics
- •Building an MDI Application
- •Built-In Capabilities of MDI Applications
- •Accessing Child Forms
- •Ending an MDI Application
- •A Scrollable PictureBox
- •Summary
- •What Is a Database?
- •Relational Databases
- •Exploring the Northwind Database
- •Exploring the Pubs Database
- •Understanding Relations
- •The Server Explorer
- •Working with Tables
- •Relationships, Indices, and Constraints
- •Structured Query Language
- •Executing SQL Statements
- •Selection Queries
- •Calculated Fields
- •SQL Joins
- •Action Queries
- •The Query Builder
- •The Query Builder Interface
- •SQL at Work: Calculating Sums
- •SQL at Work: Counting Rows
- •Limiting the Selection
- •Parameterized Queries
- •Calculated Columns
- •Specifying Left, Right, and Inner Joins
- •Stored Procedures
- •Summary
- •How About XML?
- •Creating a DataSet
- •The DataGrid Control
- •Data Binding
- •VB.NET at Work: The ViewEditCustomers Project
- •Binding Complex Controls
- •Programming the DataAdapter Object
- •The Command Objects
- •The Command and DataReader Objects
- •VB.NET at Work: The DataReader Project
- •VB.NET at Work: The StoredProcedure Project
- •Summary
- •The Structure of a DataSet
- •Navigating the Tables of a DataSet
- •Updating DataSets
- •The DataForm Wizard
- •Handling Identity Fields
- •Transactions
- •Performing Update Operations
- •Updating Tables Manually
- •Building and Using Custom DataSets
- •Summary
- •An HTML Primer
- •HTML Code Elements
- •Server-Client Interaction
- •The Structure of HTML Documents
- •URLs and Hyperlinks
- •The Basic HTML Tags
- •Inserting Graphics
- •Tables
- •Forms and Controls
- •Processing Requests on the Server
- •Building a Web Application
- •Interacting with a Web Application
- •Maintaining State
- •The Web Controls
- •The ASP.NET Objects
- •The Page Object
- •The Response Object
- •The Request Object
- •The Server Object
- •Using Cookies
- •Handling Multiple Forms in Web Applications
- •Summary
- •The Data-Bound Web Controls
- •Simple Data Binding
- •Binding to DataSets
- •Is It a Grid, or a Table?
- •Getting Orders on the Web
- •The Forms of the ProductSearch Application
- •Paging Large DataSets
- •Customizing the Appearance of the DataGrid Control
- •Programming the Select Button
- •Summary
- •How to Serve the Web
- •Building a Web Service
- •Consuming the Web Service
- •Maintaining State in Web Services
- •A Data-Driven Web Service
- •Consuming the Products Web Service in VB
- •Summary

BUILDING USER-DRAWN CONTROLS 405
Building User-Drawn Controls
This is the most complicated, but also the most flexible, type of control. A user-drawn control consists of a UserControl object with no constituent controls. You are responsible for updating the control’s visible area with the appropriate code, which must appear in the control’s OnPaint method. This method is called right before the OnPaint event is fired, and if you override it, you can take control of the repaint process.
To demonstrate the design of user-drawn controls, we’ll develop the Label3D control, which is an enhanced Label control and is shown in Figure 9.5. It provides all the members of the Label control plus a few highly desirable new features, such as the ability to align the text in all possible ways on the control, as well as in three-dimensional type. The new custom control is called Label3D, and its project on the CD is the FlexLabel project. It contains the Label3D project (which is a Windows Control Library project) and the usual test project (which is a Windows Application project).
Figure 9.5
The Label3D control is an enhanced Label control.
At this point, you’re probably thinking about the code that aligns the text and renders it as carved or raised. A good idea is to start with a Windows project, which displays a string on a form and aligns it in all possible ways. A control is an application packaged in a way that allows it to be displayed on a form instead of on the Desktop. As far as the functionality is concerned, in most cases it can be implemented on a regular form.
Designing a Windows form with the same functionality is fairly straightforward. You haven’t seen the drawing methods yet, but this control doesn’t involve any advanced drawing techniques. All we need is a method to render strings on the control. To achieve the 3D effect, you must display the same string twice, first in white and then in black on top of the white. The two strings must be displaced slightly, and the direction of the displacement determines the effect (whether the text will appear as raised or carved). The amount of displacement determines the depth of the effect. Use a displacement of one pixel for a light effect and a displacement of two pixels for a heavy one.
VB.NET at Work: The Label3D Control
The first step in designing a user-drawn custom control is to design the control’s interface: what it will look like when placed on a form (its visible interface) and how developers can access this functionality through its members (the programmatic interface). Sure, you’ve heard the same advice over and over, and many of you still start coding an application without spending much time designing it. In the real world, especially if you are not a member of programming team, people design as they code (or the other way around).
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

406 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
The situation is quite different with Windows controls. Your custom control must provide properties, which will be displayed automatically in the Properties window. The developer should be able to adjust every aspect of the control’s appearance by manipulating the settings of these properties. In addition, developers expect to see the standard properties shared by most controls (such as the background color, the text’s font, and so on). You must carefully design the methods so that they expose all the functionality of the control that should be accessed from within the application’s code, and the methods shouldn’t overlap. Finally, you must provide the events necessary for the control to react to external events. Don’t start coding a custom control unless you have formulated a very clear idea of what the control will do and how it will be used by developers at design time.
The Label3D Control’s Specifications
The Label3D control displays a caption like the standard Label control, so it must provide the Caption and Font properties, which let the developer determine the text and its appearance. The UserControl object exposes these two properties, so we need not implement them in our code. In addition, the Label3D can align its caption both vertically and horizontally. This functionality will be exposed by the Alignment property, whose settings are shown in Table 9.1.
Table 9.1: The Settings of the Alignment Property (The Align Enumeration)
Value
TopLeft
TopMiddle
TopRight
CenterLeft
CenterMiddle
CenterRight
BottomLeft
BottomMiddle
BottomRight
The (self-explanatory) values in Table 9.1 are the names that will appear in the drop-down list of the Alignment property in the Properties window. As you have noticed, properties with a limited number of settings display a drop-down list there. This list contains descriptive names (instead of numeric values), and the developer can select only a valid setting. The Alignment property’s settings will be the members of a custom enumeration.
Similarly, the text effect is manipulated through the Effect property, whose settings are shown in Table 9.2. There are basically two types of effects, raised and carved text, and two variations on each effect (normal and heavy).
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

BUILDING USER-DRAWN CONTROLS 407
Table 9.2: The Settings of the Effect Property (The Effect3D Enumeration)
Value
None
Carved
CarvedHeavy
Raised
RaisedHeavy
Like the Alignment property, the Effect property has a small number of valid settings, which will be identified in the Properties window with descriptive names. These names are the members of another custom enumeration.
In addition to the custom properties, the Label3D control should also expose the standard properties of a Label control, such as Tag, BackColor, and so on. Developers expect to see standard properties in the Properties window, and you should implement them. The Label3D control doesn’t have any custom methods, but it should provide the standard methods of the Label control, such as the Move method. Similarly, although the control doesn’t raise any special events, it must support the standard events of the Label control, such as the mouse and keyboard events.
Most of the custom control’s functionality exists already, and there should be a simple technique to borrow this functionality from other controls, rather than implementing it from scratch. This is indeed the case: The UserControl object, from which all user-drawn controls inherit, exposes a large number of members.
Designing the Custom Control
Start a new project of the Windows Control Library type, name it FlexLabel, and then rename the UserControl1 object to Label3D. Open the UserControl object’s code window and change the name of the class from UserControl1 to Label3D. The first two lines in the code window should be:
Public Class Label3D
Inherits System.Windows.Forms.UserControl
All user-drawn controls inherit from the UserControl object, and you will soon see the members exposed by the UserControl object itself.
Note Every time you place a Windows control on a form, it’s named according to the UserControl object’s name and a sequence digit. The first instance of the custom control you place on a form will be named Label3D1, the next one will be named Label3D2, and so on. Obviously, it’s important to choose a meaningful name for your UserControl object.
As you will soon see, the UserControl is the “form” on which the custom control will be designed. It looks, feels, and behaves like a regular VB form, but it’s called a UserControl. UserControl objects have additional unique properties that don’t apply to a regular form, but in order to start designing new controls, think of them as regular forms.
You’ve set the scene for a new user-drawn Windows control. Start by declaring the two enumerations shown in Tables 9.1 and 9.2. Listing 9.6 shows the Enum statements for the two enumerations.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

408 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
Listing 9.6: The Align and Effect3D Enumerations
Public Enum Align
TopLeft
TopMiddle
TopRight
CenterLeft
CenterMiddle
CenterRight
BottomLeft
BottomMiddle
BottomRight
End Enum
Public Enum Effect3D
None
Raised
RaisedHeavy
Carved
CarvedHeavy
End Enum
The next step is to implement the Alignment and Effect properties. Each property’s type is an enumeration, and Listing 9.7 shows the implementation of the two properties.
Listing 9.7: The Alignment and Effect Properties
Private Shared mAlignment As Align
Private Shared mEffect As Effect3D
Public Property Alignment() As Align
Get
Alignment = mAlignment
End Get
Set(ByVal Value As Align)
mAlignment = Value
Invalidate()
End Set
End Property
Public Property Effect() As Effect3D
Get
Effect = mEffect
End Get
Set(ByVal Value As Effect3D)
mEffect = Value
Invalidate()
End Set
End Property
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

BUILDING USER-DRAWN CONTROLS 409
The current settings of the two properties are stored in the private variables mAlignment and mEffect. When either property is set, the Property procedure’s code calls the Invalidate method of the UserControl object to redraw the string on the control’s surface. The call to the Invalidate method is required for the control to operate properly in design mode. You can provide a method to redraw the control at runtime (although developers shouldn’t have to call a method to refresh the control every time they set a property), but this isn’t possible at design time. When a property is changed in the Properties window, the control should be able to update itself and reflect the new property setting. The Invalidate method causes the control to be redrawn, to reflect the new setting of the property. Shortly, you’ll see an even better way to automatically redraw the control every time a property is changed.
Finally, you must add one more property, the Caption property, which is the string to be rendered on the control. Declare a private variable to store the control’s caption (the mCaption variable) and enter the code from Listing 9.7 to implement the Caption property.
Listing 9.7: The Caption Property Procedure
Private mCaption As String
Property Caption() As String
Get
Caption = mCaption
End Get
Set(ByVal Value As String)
mCaption = Value
Invalidate()
End Set
End Property
The core of the control’s code is in the OnPaint method, which is called automatically before the control repaints itself (that is, prior to the Paint event). The same event’s code is also executed when the Invalidate method is called, and this is why we call this method every time one of the control’s properties changes value. The OnPaint method enables you to take control of the repaint process and supply your own code for repainting the control’s surface. The single characteristic of all userdrawn controls is that they override the default OnPaint method. This is where you must insert the code to draw the control’s surface—i.e., draw the specified string taking into consideration the Alignment and Effect properties. The OnPaint method’s code is shown in Listing 9.8.
Listing 9.8: Overriding the UserControl’s OnPaint Method
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs) Dim lblFont As Font = Me.Font
Dim lblBrush As New SolidBrush(Color.Red) Dim X, Y As Integer
Dim textSize As SizeF
textSize = e.Graphics.MeasureString(mCaption, lblFont) Select Case Me.mAlignment
Case Align.BottomLeft
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

410 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
X = 2
Y = Me.Height - textSize.Height Case Align.BottomMiddle
X = CInt((Me.Width - textSize.Width) / 2) Y = Me.Height - textSize.Height
Case Align.BottomRight
X = Me.Width - textSize.Width - 2 Y = Me.Height - textSize.Height
Case Align.CenterLeft X = 2
Y = (Me.Height - textSize.Height) / 2 Case Align.CenterMiddle
X = (Me.Width - textSize.Width) / 2 Y = (Me.Height - textSize.Height) / 2
Case Align.CenterRight
X = Me.Width - textSize.Width - 2
Y = (Me.Height - textSize.Height) / 2 Case Align.TopLeft
X = 2
Y = 2
Case Align.TopMiddle
X = (Me.Width - textSize.Width) / 2 Y = 2
Case Align.TopRight
X = Me.Width - textSize.Width - 2 Y = 2
End Select
Dim dispX, dispY As Integer Select Case mEffect
Case Effect3D.None : dispX = 0 : dispY = 0 Case Effect3D.Raised : dispX = 1 : dispY = 1
Case Effect3D.RaisedHeavy : dispX = 2 : dispY = 2 Case Effect3D.Carved : dispX = -1 : dispY = -1 Case Effect3D.CarvedHeavy : dispX = -2 : dispY = -2
End Select e.Graphics.Clear(Me.BackColor) lblBrush.Color = Color.White
e.Graphics.DrawString(mCaption, lblFont, lblBrush, X, Y) lblBrush.Color = Me.ForeColor
If Me.DesignMode Then
e.Graphics.DrawString(“DesignTime”, New Font(“Verdana”, 24, _ FontStyle.Bold), New SolidBrush(Color.FromARGB(200, 230, 200, 255)), 0, 0)
Else
e.Graphics.DrawString(“RunTime”, New Font(“Verdana”, 24, FontStyle.Bold), _ New SolidBrush(Color.FromARGB(200, 230, 200, 255)), 0, 0)
End If
e.Graphics.DrawString(mCaption, lblFont, lblBrush, X + dispX, Y + dispY) End Sub
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

BUILDING USER-DRAWN CONTROLS 411
This subroutine calls for a few explanations. The OnPaint event passes a PaintEventArgs argument (the ubiquitous e argument). This argument exposes the Graphics property, which represents the control’s surface. The Graphics object exposes all the methods you can call to create graphics on the control’s surface. The Graphics object is discussed in detail in Chapter 14, but for the purposes of this chapter all you need to know is that the MeasureString method returns the dimensions
of a string when rendered in a specific font, and the DrawString method draws the string in the specified font. The first Select Case statement calculates the coordinates of the string’s origin on the control’s surface. These coordinates are calculated for each different alignment. Then another Select Case statement sets the displacement between the two strings, so that when superimposed they produce a three-dimensional look. Finally, the code draws the value of the Caption property on the Graphics object. It draws the string in white color first, then in black. The second string is drawn dispX pixels to the left and dispY pixels below the first one to give the 3D effect.
Notice the two statements that print the strings “DesignTime” and “RunTime” in a light color on the control’s background, depending on the current status of the control. They indicate whether the control is currently in design (if UserMode is True) or run time (if UserMode is False).
Testing Your New Control
To test your new control, you must first add it to the Toolbox, so that you can place it on a form. You can add a form to the current project and test the control, but you shouldn’t add more components to the control project. It’s best to add a new project to the current solution.
Add the TestProject to the current solution, rename its Form to TestForm, and open it in design mode. Place a Label3D control on the test form and the other controls shown in Figure 9.5. If the Label3D icon doesn’t appear in the Toolbox, you must build the control’s project.
Now double-click the Label3D control on the form to see its events. Your new control has its own events, and you can program them just as you would program the events of any other control. Enter the following code in the control’s Click event:
Private Sub Label3D1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Label3D1.Click MsgBox(“My properties are “ & vbCrLf & _
“Caption = “ & Label3D1.Caption & vbCrLf & _ “Alignment = “ & Label3D1.Alignment & vbCrLf & _ “Effect = “ & Label3D1.Effect)
End Sub
To run the control, press F5 and then click the control. You will see the control’s properties displayed in a message box.
The other controls on the test form (see Figure 9.5) allow you to set the appearance of the custom control at runtime. The two ComboBox controls are populated with the members of the appropriate enumeration when the form is loaded. In their SelectedIndexChanged event handler, you must set the corresponding property to the selected value, as shown in the following listing:
Private Sub AlignmentBox_SelectedIndexChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _
Handles AlignmentBox.SelectedIndexChanged Label3D1.Alignment = AlignmentBox.SelectedItem
End Sub
Private Sub EffectsBox_SelectedIndexChanged(ByVal sender As System.Object, _
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

412 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
ByVal e As System.EventArgs) _
Handles EffectsBox.SelectedIndexChanged
Label3D1.Effect = EffectsBox.SelectedItem
End Sub
The TextBox control at the bottom of the form stores the Caption property. Every time you change this string, the control is updated, because the Set procedure of the Caption property calls the Invalidate method.
Initializing a Custom Control
To initialize the control’s properties, insert the appropriate code in the New() subroutine. This subroutine is in the section marked with the following line:
#Region “ Windows Form Designer generated code “
and it contains code generated by the designer. Expand this section by clicking the plus sign in front of its name and locate the New() subroutine. Listing 9.9 shows the New() subroutine of the custom control.
Listing 9.9: The New() Subroutine of the Label3D Control
Public Sub New()
MyBase.New()
‘This call is required by the Windows Form Designer. InitializeComponent()
‘Add any initialization after the InitializeComponent() call mCaption = “Label3D”
mAlignment = Align.CenterMiddle mEffect = Effect3D.Raised
SetStyle(ControlStyles.ResizeRedraw, “True”) End Sub
I’ve only added the four last statements in this listing; the first couple of statements and the comments were inserted by the Designer. After assigning initial values to the private variables that store the control’s properties, there’s a call to the SetStyle method, which accepts several arguments. The ResizeRedraw argument determines whether the control will be redrawn when it’s resized. Normally, the Paint event isn’t fired when the control is made smaller, and when the control is enlarged, only the new area of the control is repainted. The call to the SetStyle method forces the control to be repainted every time the user resizes it on the form.
You need not initialize all the properties of the control, just the ones that should have a value the first time the control is placed on a form. When you place a TextBox control on the form, for example, its Text property is set to the control’s name and its Font property is set to Microsoft Sans Serif. The Label3D control’s default caption is “Label3D”. Many of the control’s properties are handled by the UserControl object itself, and Font is one of them. To set the initial font, locate the Font property of the UserControl and set it accordingly. The BackgroundColor and BackgroundImage properties are also handled by the UserControl object. You can specify a default background image if you want, but this property will be exposed in the Properties window, and the developer can set it at design time.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |