You can create a method in your ActiveX control as easily as you create a function or subroutine in Visual Basic; the syntax is the same, because any public function or subroutine in your control is exposed as a method.
Methods are the verbs of your control, just as properties are the adjectives. Users expect methods to perform processing or other actions. Accordingly, you should carefully consider whether the method you're looking to implement should really be implemented as a property. Putting yourself in the user's shoes and comparing your control to other controls that provide similar functionality will go a long way toward resolving the way your interface should be presented.
Bear in mind that it is permissible for methods to automatically adjust properties, although you should avoid doing this if it makes your interface incomprehensible. One example of a method that can change a property is the Move method, which most controls expose. The Move method not only determines where a control is positioned relative to its container, but it can also alter the control's size. The Move method has the ability, then, to change the Top, Left, Height, and Width properties of a control. Giving your methods the ability to alter properties of your control isn't always necessary to do, but it can make your control easier to program.
This section demonstrates how to implement a simple method in your control. For this demonstration, we'll use a new version of the HappyHour control you worked on in previous chapters. You'll find this control on your CD-ROM.
The procedure you'll create will display the amount of time between now and happy hour. This procedure will be exposed in the HappyHour control as the WatchClock method. When this method is executed, the control will display a message box informing the user how much time remains until happy hour. To add this method to the HappyHour control:
Public Sub WatchClock() Dim lMinutesLeft As Long If mHappyHourBegin = "" Or mHappyHourEnd = "" Then MsgBox "I have no idea when happy hour is.", _ vbExclamation, _ "Something's Wrong" Exit Sub End If If Time > mHappyHourBegin And Time < mHappyHourEnd Then ' it's already happy hour MsgBox "It's happy hour! What are you doing here?", _ vbExclamation, _ "Party Time!" Exit Sub Else ' calculate and display the number ' of hours until happy hour lMinutesLeft = DateDiff("n", Time, mHappyHourBegin) MsgBox "Happy hour is in " & lMinutesLeft _ & " minutes!", _ vbInformation, _ "Get Back To Work!" End If End Sub
Be very careful that you declare this sub as Public. Subs declared as Private aren't exposed as methods of the property.
The code you just entered does the following:
Since methods are only executable at runtime, you will have to put the EXE project form into run mode and use the Immediate window to test your new method. To do this:
HappyHour1.HappyHourBegin = #11:49:00 AM#
HappyHour1.HappyHourEnd = #12:30:00 PM#
The message box displayed by the HappyHour control depends on what time it is. If it is currently happy hour, you'll see the message box displayed as in Figure 6.1.
Figure 6.1 : It's happy hour.
If it's not happy hour, the control will determine how many minutes until it's happy hour and display the message shown in Figure 6.2.
Figure 6.2 : Minutes until happy hour.
You'll recall that in Visual Basic, the standard delimiter for date and time values is the pound sign (#). This is analogous to the double-quotation sign that delimits a string.
You can pass an argument to a method the same way you pass an argument to any other procedure. Passing an argument to a procedure gives the procedure additional information about how to perform its processing.
For example, consider the Move method that exists in most controls. The syntax of this method is:
object.Move [left, top, width, height]
To use this method to move a command button called cmdSpeedy, you'd use the code:
cmdSpeedy.Move 100, 100
(The width and height arguments are omitted in this code because you're only looking to move, rather than resize, the control.)
You can add arguments to your event procedures to make them work the same way the Move event does. In the following example, you'll learn how to create a method that takes an argument by adding another method to the HappyHour project. This method, called HappyAlert, generates one or more audible tones that get the user's attention. The number of audible tones generated by the HappyAlert event is determined by the integer argument passed to it; if you write the code HappyHour1.HappyAlert 5, the control will beep five times.
Here's the code for the HappyAlert method. This code is designed to be included in the HappyHourChanged event; to include it, type the following code in the code window of the HappyHour user control:
Public Sub HappyAlert(iAlertNumber As Integer) Dim iCounter As Integer For iCounter = 1 To iAlertNumber Beep Next iCounter End Sub
To test this code, do the following:
You should be able to hear your computer beep five times.
If your computer uses a .WAV file as standard system beep, you may not hear exactly five beeps. This is because the Beep statement isn't capable of beeping synchronously; it simply tells the operating system to beep as fast as it can. If you don't hear five beeps, the system may be playing them so fast that the first beep doesn't have a chance to finish before the second and third beeps get going. You have the ability, however, to make the operating system play sounds in a more intelligent manner through the use of API calls. We'll discuss how to make API calls in Chapter 11, "Windows API and DLL Calls."
Just as functions return values, methods can return values as well. Methods that return values don't occur very frequently in the Visual Basic world, but when they do, they can be very powerful.
If your method returns a value, you can use the value it returns in other code. For example, consider the OpenRecordset method of the Database object. This method is used to return a reference to a table, query, or another set of records in a database. The method takes a table name or query name as a parameter and returns a Recordset object variable.
A typical OpenRecordset method looks like this:
Dim MyDatabase As Database Dim MyRecordset As Recordset Set MyRecordset = MyDatabase.OpenRecordset("tblAddress")
The whole purpose of the OpenRecordset method, then, is to put the object that represents the table called tblAddress into the object variable MyRecordset.
This brings up an interesting point about methods that return values: If a method can act like a function and return a value, what is the difference between a method that returns a value and a property?
It makes sense that a database would have an OpenRecordset method rather than a Recordset property, since a single database can produce a potentially infinite number of recordsets. (This is not only because databases can have multiple tables, but because a Recordset object can point to any subset of records in a single table.)
To my way of thinking, for most purposes you're likely to run across, there isn't much functional difference between a property and a method that returns a value, except that a method that returns a value is a strange animal. Most Visual Basic programmers aren't expecting your methods to return values, so if you write a bunch of methods that return values, most Visual Basic programmers are going to become confused.
This means if you're ever tempted to include a property-like method in your control, you should strongly consider writing the procedure as a property instead. Properties are much easier for the average Visual Basic user to understand, they're much more commonly used, and they're generally more hygienic and morally upright.
Because you might have a case where you want to write a method that acts as a property, I'll toss out a demonstration of how to do it. The demonstration will calculate the number of minutes between now and happy hour in the form of a method called TellHowLong. Bear in mind this method could just as well be written as a read-only property.
The CalculateHowLong method won't be nearly as complicated to call as the OpenRecordset method of the Database object; in fact, it will be nearly identical to the WatchClock method. But instead of notifying the user with a message box saying how many minutes until happy hour, it will return the number of minutes as a long integer to the calling procedure (rather than displaying it on the screen, as in the previous example). If it is currently happy hour, the method returns the value True (or -1). To do this:
Public Function CalculateHowLong() ' Returns the number of minutes ' until happy hour (similar to ' WatchClock). If mHappyHourBegin = "" Or mHappyHourEnd = "" Then CalculateHowLong = False Exit Function End If If Time > mHappyHourBegin And Time < mHappyHourEnd Then ' It's already happy hour CalculateHowLong = True Exit Function Else ' Calculate and return the number ' of minutes until happy hour CalculateHowLong = DateDiff("n", Time, mHappyHourBegin) End If End Function
Don't forget to declare methods that return values as Public Functions.
You can see this code does almost the same thing as the WatchClock method, except instead of displaying a message box, it returns a value. The user could then use that value to take an action (such as displaying a message, updating a status label, or running some other code). Limiting the actions of your control in this way gives the user more flexibility.
To demonstrate that your new method returns the number of minutes until happy hour, do the following:
HappyHour1.HappyHourBegin = #5:00:00 PM#
HappyHour1.HappyHourEnd = #6:00:00 PM#
The number of minutes between now and happy hour should be displayed in the Immediate window, unless it is currently happy hour, in which case the value True will be displayed.
Although methods tend to be used less often than properties, certain methods should always be available. As a minimum, you should consider exposing the following methods in any control you create:
In general, you should think about controls that are similar to your control and expose as many methods as similar controls expose. For example, if your control works like a command button, consider exposing as many of the command button's methods as possible (such as Move, Drag, and SetFocus). In general, try to surprise the user of your control as little as possible; if you leave something out, have a good reason for doing so.
If your control is comprised of constituent controls, you should consider exposing as many methods of those constituent controls as possible, as long as it makes sense to do so.
For example, in order to control the height, width, and placement of your control, you should expose a Move method. (You can simply expose Height, Width, Top, and Left properties, but a Move method is faster to execute and easier to code.)
Often, you'll find yourself merging the methods of two or more constituent controls embedded in your control. For example, if the HappyHour control had a Move method, it would control the Height and Width properties of both its constituent Label control as well as its PictureBox control.
Any method can be mapped from a constituent control to your UserControl, either manually or through the ActiveX Control Interface Wizard. See Chapter 2 "Control Basics," for information on how to use the ActiveX Control Interface Wizard.
As a bare minimum, if your control is visible at runtime, it should expose a Refresh method. You can implement the Refresh method using the Refresh method of the UserControl object.
Just as properties and events are provided by the Extender object of the container, a number of methods are provided by the container as well. Because these methods are provided by the container, users can call them to manipulate your control; you do not have to write code to expose these methods.
In Visual Basic, the following methods are provided by the container:
Because they are supplied by the container, you do not need to write any code to make these methods available in your control; they are always available when your control is placed in a Visual Basic form. Remember, though, that because these methods are provided by the control's container, you cannot depend on these methods being available to you in all containers.
For more information on the container, see Chapter 7 "Interacting with the Container." For information on error-handling, see Chapter 15, "Debugging and Error Trapping."
In this chapter, you learned how to write public subroutines and functions that are exposed as methods in your control. In the next chapter, you'll learn how to take advantage of the container to provide additional functionality to your ActiveX control.