
- •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 413
The Changed Events
The UserControl object exposes many of the events you need to program the control, like the key and mouse events. In addition, you can raise custom events. The .NET Windows controls raise an event every time a property value is changed. If you examine the list of events exposed by the Label3D control, you’ll see the FontChanged and SizeChanged events. These events are provided by the UserControl object. As a control developer, you should expose similar events for your custom properties. This isn’t very difficult to do, but you must follow a few steps.
Declare an event handler for each of the Changed events:
Private mOnAlignmentChanged As EventHandler
Private mOnEffectChanged As EventHandler
Private mOnCaptionChanged As EventHandler
Then declare the actual events and their handlers:
Public Event AlignmentChanged(ByVal sender As Object, ByVal ev As EventArgs) Public Event EffectChanged(ByVal sender As Object, ByVal ev As EventArgs) Public Event CaptionChanged(ByVal sender As Object, ByVal ev As EventArgs)
And finally invoke the event handlers from within the appropriate OnEventName method:
Protected Overridable Sub OnAlignmentChanged(ByVal E As EventArgs)
Invalidate()
If Not (mOnAlignmentChanged Is Nothing) Then mOnAlignmentChanged.Invoke(Me, E)
End Sub
Protected Overridable Sub OnEffectChanged(ByVal E As EventArgs)
Invalidate()
If Not (mOnEffectChanged Is Nothing) Then mOnEffectChanged.Invoke(Me, E)
End Sub
Protected Overridable Sub OnCaptionChanged(ByVal E As EventArgs)
Invalidate()
If Not (mOnCaptionChanged Is Nothing) Then mOnCaptionChanged.Invoke(Me, E)
End Sub
As you can see, the OnpropertyChanged events call the Invalidate method to redraw the control when a property’s value is changed. As a result, you can now remove the call to the Invalidate method from the Property Set procedures. If you switch to the test form, you will see that the custom control exposes the AlignmentChanged, EffectChanged, and CaptionChanged events. The OnCaptionChanged method is executed automatically every time the Caption property changes value, and it fires the CaptionChanged event. Normally, this event isn’t programmed.
Raising Events
The UserControl object raises the usual events you’d expect to see in the editor’s window. When you select the custom control in the Objects drop-down list of the editor and expand the list of events for this control, you’ll see all the events fired by UserControl. They’re the usual events, which you already know how to program. However, what good are the Key events if the custom control doesn’t handle keystrokes? Most events will go unnoticed in most applications.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

414 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
The situation is very different with compound controls. A compound control usually allows the user to interact with one or more of its constituent controls. Let’s return to the ColorEdit custom control. The Click event is fired when the user clicks any area of the control outside the compound controls. When one of the scroll bars is clicked, no event is raised. Instead, the control adjusts the selected color. You can raise an event from within your control, if you want to. For example, you can raise an event to notify the application that the red scroll bar control has changed value, or that another color was selected in the ComboBox control with the named colors. Of course, these events are of questionable value, because the motivation for building a custom control is to hide as many of the low-level details as possible.
To demonstrate how to raise custom events, let’s say you want to raise an event when the user clicks the Label control where the selected color is displayed. Let’s call this event ColorClick. To raise a custom event, you must declare it in your control and call the RaiseEvent method to raise it. Note that the same event may be raised from many different places in the control’s code.
To declare the ColorClick event, enter the following statement in the control’s code. This line can appear anywhere, but placing it after the private variables that store the property values is customary.
Public Event ColorClick(ByVal sender As Object, ByVal e As EventArgs)
To raise the ColorClick event when the user clicks the Label control, insert the following statement in the Label control’s Click event handler:
Private Sub Label1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Label1.Click
RaiseEvent ColorClick(Me, e)
End Sub
Raising a custom event from within a control is as simple as raising an event from within a class. The RaiseEvent statement in the Label’s Click event handler maps the Click event of the Label control to the ColorClick event of the custom control. If you switch to the test form and examine the list of events of the Label3D control on the form, you’ll see that the new event was added.
The ColorClick event doesn’t convey much information. You could use it to display a context menu with a few common color names and let the user select one. In a real application, you could convey a lot of information to the developer using your control through custom events. As you can see, the arguments passed to the application by the ColorClick event are the same as the arguments passed to the Label control’s Click event.
When raising custom events, it’s likely that you’ll want to pass additional information to the developer. Let’s say you want to pass the Label control’s color to the application through the second argument of the ColorClick event. The EventArgs type doesn’t provide a Color property, so we must build a new type that inherits all the members of the EventArgs type and adds a property, the Color property. You can probably guess that we’ll create a custom class that inherits from the EventArgs class and adds the Color member.
Enter the statements of Listing 9.10 at the end of the file (after the existing End Class statement).
Listing 9.10: Declaring a Custom Event Type
Public Class ColorEvent
Inherits EventArgs
Public Shared color As Color
End Class
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

BUILDING USER-DRAWN CONTROLS 415
Then, declare the following event in the control’s code:
Public Event ColorClick(ByVal sender As Object, ByVal e As ColorEvent)
And finally raise the ColorClick event from within the Label’s Click event handler (Listing 9.11).
Listing 9.11: Raising a Custom Event
Private Sub Label1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Label1.Click Dim ev As ColorEvent
ev.color = Label1.BackColor RaiseEvent ColorClick(Me, ev)
End Sub
Using the Custom Control in Other Projects
By adding a test project to the Label3D custom control project, we were able to design and test the control in the same environment. A great help indeed, but the custom control can’t be used in other projects. If you start another instance of Visual Studio and attempt to add your custom control to the toolbox, you won’t see the Label3D entry in the Toolbox.
To add your custom component in another project, open the Customize Toolbox dialog box, then click the .NET Framework Components tab. Be sure to carry out the steps described here while the .NET Framework Components tab is visible. If the COM Components tab is visible instead, you can perform the same steps but you’ll end up with an error message (because the custom component is not a COM component).
Click the Browse button on the dialog box and locate the FlexLabel.dll file. It’s in the Bin folder under the FlexLabel project’s folder. The Label3D control will be added to the list of .NET Framework components, as shown in Figure 9.6. Check the box in front of the control’s name, then click the OK button to close the dialog box and add Label3D to the Toolbox. Now you can use this control in your new project.
Figure 9.6
Adding the Label3D control to another project’s Toolbox
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

416 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
VB.NET at Work: The Alarm Control
This example demonstrates a custom control that contains all three types of members—properties, methods, and events—and raises events based on a timer, rather than some user action. It’s a simple alarm that can be set to go off at a certain time, and when it times out, it triggers a TimeOut event. Moreover, while the timer is ticking, the control updates a display, showing the time elapsed since the timer started (the property CountDown must be False) or the time left before the alarm goes off (the property CountDown must be True). Figure 9.7 shows the test form for the Alarm control. The first instance of the Alarm control counts down the time left before the alarm goes off, and the second counts the time since it was started.
Figure 9.7
The test form for the Alarm custom control
The Alarm Control’s Interface
The Alarm control has two custom properties, AlarmTime and CountDown. AlarmTime is the time when the alarm goes off, expressed in AM/PM format. CountDown is a True/False property that determines what’s displayed on the control. If CountDown is True, the alarm displays the time remaining. If you set the alarm to go off at 8:00 P.M. and you start the timer at 7:46 P.M., the control displays 0:14.00, then 0:13.59, and so on until the alarm goes off 14 minutes later. If CountDown is False, the control starts counting at 00:00.00 and counts until the AlarmTime is reached. The Alarm control takes into consideration the date as well and can be set to go off in more than 24 hours. However, it was designed to count a relatively small number of hours. If you set it to go off in a week, the number of hours left until the TimeOut event won’t be displayed nicely on the control (you have to make the control wider so that it can fit more digits), but the code will work for any setting of the AlarmTime property.
The Alarm control has two methods for starting and stopping the alarm: StartTimer starts the timer, and StopTimer stops it.
Finally, the Alarm control fires the TimeOut event, which notifies the application that the alarm has gone off (which happens when the time reaches AlarmTime). The application can use this event to trigger another action or simply to notify the user.
Testing the Alarm Control
The Alarm control’s test form is shown earlier, in Figure 9.7. It contains two instances of the control, and you set their CountDown property at design time. The AlarmTime property of both controls is set to the same value, which is 15 minutes ahead of the current time. Listing 9.12 shows the code behind the Start Timers button of the test form.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

BUILDING USER-DRAWN CONTROLS 417
Listing 9.12: Setting Up the Two Alarm Controls on the Test Form
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click CtrlAlarm1.CountDown = True
CtrlAlarm2.CountDown = False CtrlAlarm1.AlarmTime = Now.AddSeconds(10) CtrlAlarm2.AlarmTime = Now.AddSeconds(20) CtrlAlarm1.StartTimer() CtrlAlarm2.StartTimer()
TextBox1.Text = “Current date and time: “ & vbCrLf & Now & vbCrLf TextBox1.Text = TextBox1.Text + “Alarm1” & vbCrLf
TextBox1.Text = TextBox1.Text & “ |
set for “ & _ |
CtrlAlarm1.AlarmTime.ToShortDateString |
|
TextBox1.Text = TextBox1.Text & “ |
“ & _ |
CtrlAlarm1.AlarmTime.ToLongTimeString & vbCrLf |
|
TextBox1.Text = TextBox1.Text & “ |
and counting down” & vbCrLf |
TextBox1.Text = TextBox1.Text & vbCrLf & “Alarm2” & vbCrLf |
|
TextBox1.Text = TextBox1.Text & “ |
set for “ & _ |
CtrlAlarm2.AlarmTime.ToShortDateString |
|
TextBox1.Text = TextBox1.Text & “ |
“ & _ |
CtrlAlarm2.AlarmTime.ToLongTimeString & vbCrLf |
|
TextBox1.Text = TextBox1.Text & “ |
and counting up” & vbCrLf |
End Sub
The last group of statements that manipulate the TextBox control display the time each control was started and the alarm time of the two controls. Then, you can watch the alarms count the time until they go off. To start the two alarms, the code calls the control’s StartTimer method. The information printed on the TextBox will help you verify that the controls work properly, especially if you edit the code.
Both controls will fire the TimeOut event when the alarm time is reached, and I’ve inserted two very simple handlers for these events (there’s a similar event handler for the second control):
Private Sub CtrlAlarm1_TimeOut() Handles CtrlAlarm1.TimeOut
Beep()
MsgBox(“Alarm1 is off!”)
End Sub
Designing the Alarm’s User Interface
Your first step is to design the control’s interface. Unlike the Timer control of Visual Basic, the Alarm control has a visible interface and uses two constituent controls: a Timer control (which is used to update the display every second as well as figure out whether the alarm must go off or not) and a Label control, where it displays the time.
To design the control’s interface, follow these steps:
1.Place a Label control on the UserControl form, and set its Font property to a font and size that looks nice for our purposes. We will not expose the Label’s Font as a property of the
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

418 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
control, so that developers using this control can’t change it. If you want developers to be able to change the control’s font, you must insert additional code to adjust the dimensions of the Label so that all the digits will be visible.
2.Set the Label’s Dock property to Fill, so that it takes up all the space provided for the control.
3.Add a Timer control to the UserControl object—it will appear in the components tray at the bottom of the Designer’s window.
The control’s visible interface is quite trivial, thanks to the constituent controls. Let’s look at the members of the Alarm control (Figure 9.8).
Figure 9.8
The Alarm control at design time
Implementing the Control’s Members
Now we are ready to implement the control’s properties, methods, and event. Let’s start with the properties. First, declare the private variables that will hold the property values:
Private startTime As Date
Private Running As Boolean
Private m_CountDown As Boolean
Private m_AlarmTime As Date
As you have guessed, m_CountDown and m_AlarmTime are the two private variables that will hold the values of the CountDown and AlarmTime properties. The Running variable is True while the alarm is running and is declared outside any procedure so that all procedures can access its value. The startTime variable is set to the time the alarm starts counting and is used when the control is not counting down (you’ll see how it’s used shortly).
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

BUILDING USER-DRAWN CONTROLS 419
The procedures for implementing the control’s properties are quite simple; they’re detailed in Listing 9.13.
Listing 9.13: The Alarm Control’s Properties
Public Property CountDown() As Boolean
Get
CountDown = m_CountDown
End Get
Set(ByVal vNewValue As Boolean)
m_CountDown = vNewValue
End Set
End Property
Public Property AlarmTime() As Date
Get
AlarmTime = m_AlarmTime
End Get
Set(ByVal vNewValue As Date)
If IsDate(vNewValue) Then m_AlarmTime = vNewValue
End Set
End Property
The AlarmTime property may include a date part. If you specify a date only, the program assumes that the time is 00:00:00 (midnight). In the Properties window, VB will display a date value for the AlarmTime property. Type the desired time after the date in the format “hh:mm:ss”. Notice that because AlarmTime is of the Date type, a DateTimePicker control will be automatically displayed on the Properties window to help you set the property’s value visually.
Now we can add the code for the two methods. The StartTimer method (Listing 9.14) sets the Timer control’s Enabled property to True, so that it will start firing Tick events.
Listing 9.14: The StartTimer Method
Public Sub StartTimer()
If Not Running Then Timer1.Enabled = True Running = True startTime = Now
End If End Sub
This method doesn’t do anything if the alarm is already running. The StopTimer method is shown in Listing 9.15.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

420 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS
Listing 9.15: The StopTimer Method
Public Sub StopTimer()
If Running Then
Timer1.Enabled = False
Running = False
End If
End Sub
As with the StartTimer method, the alarm stops only if it’s running. If that’s the case, the code disables the Timer control and sets the Running variable to False.
Next declare the TimeOut event with the following statement, which must appear outside any procedure.
Public Event TimeOut()
The TimeOut event doesn’t pass any information to the caller; it simply notifies the application that the current instance of the Alarm control has timed off. To raise the event, you must insert the appropriate code in the Timer’s Tick event handler. In the same event handler, which is invoked every second, we must also update the display. If the control is counting down, we create a TimeSpan object with the difference between the current time and the AlarmTime property. If the control is counting up, we create another TimeSpan object with the difference between the time the control was started and the current time (the time elapsed since the alarm was started). Listing 9.16 is the code of the Timer control’s Tick event hander.
Listing 9.16: The Timer’s Tick Event Handler
Private Sub Timer1_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Timer1.Tick
Dim TimeDiff As TimeSpan
TimeDiff = m_AlarmTime.Subtract(Now)
If TimeDiff.Seconds < 0 Then
StopNow = True
Timer1.Enabled = False
Label1.Text = “*****”
RaiseEvent TimeOut
Exit Sub
End If
If Not m_CountDown Then
‘the following statement calculates the difference
‘between current time and alarm time and adds 1 second TimeDiff = Now.TimeOfDay.Subtract(startTime.TimeOfDay). _
Add(New TimeSpan(0, 0, 1))
End If
Label1.Text = Format(TimeDiff.TotalHours, “00”) & “:” & _
Format(TimeDiff.Minutes, “00”) & “:” & _
Format(TimeDiff.Seconds, “00”)
End Sub
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |