Handling Events in Visual Basic Applications
How do you trap events in objects contained by WebBrowser objects in a way that enables you to handle the events with native Microsoft Visual Basic code? This article explains how to create a Visual Basic-based application that consists of a class module and a form that contains a WebBrowser object. Members of the class trap events from an HTML document object contained by the WebBrowser object, then "forward" the events to a procedure in the form. Visual Basic code in the form can then execute in response to the forwarded events.
- Implementation Steps
- Run Your Project
- Beyond the Basics
- Related Topics
Applications based on Visual Basic can contain WebBrowser objects, which can contain other objects—such as HTML document objects. WebBrowser objects, however, do not provide a way to trap all of the events that can occur in the objects they can contain. The WebBrowser control, for example, does not provide a way to trap the onclick event of a button element in an HTML document object the control contains.
Events in HTML document objects are forwarded up the document object's hierarchy until the top of the hierarchy is reached (or until event forwarding is canceled). This forwarding is referred to in Understanding the Event Model as bubbling. But events do not bubble to the document object's container. The top of an HTML document object's hierarchy is the document object. The WebBrowser control does not provide a way to trap the onclick event of a button element because the event does not bubble to the WebBrowser control.
If you use Dynamic HTML (DHTML), you can use the attachEvent method to bind functions to events, ensuring that a specified function is called whenever a particular event fires. This enables you to handle events in HTML document objects with code that's also included in the document object. But this approach limits you to DHTML and script. And the code in the document object is isolated from the container (and its container).
Event sinks can be used in Visual Basic-based applications to create customized ways of responding to events that are triggered by Microsoft ActiveX objects such as WebBrowser objects. This approach often uses WithEvents variables to handle events triggered by the ActiveX objects. But if the events don't bubble to the host applications, you can't trap the events. If you can't trap the events, you can't write procedures to handle them.
You must use Visual Basic 6.0 or later to develop this application. The CallByName function, which this application uses to bind a method at run time, is not supported by earlier versions of Visual Basic. Microsoft Internet Explorer 4.0 or later must also be installed. The innerHTML property, which this application uses to create an HTML document object inside the WebBrowser control, is not supported by earlier versions of Windows Internet Explorer.
The instructions in this tutorial are written for all levels of developers who use Visual Basic. If you are familiar with the terminology of object-oriented programming—terms such as properties, methods, and events—you should be able to follow the steps in this tutorial. Familiarity with HTML, DHTML, and the document object model (DOM) is also useful.
Begin by creating a standard EXE project. This project consists of a class module and a single Visual Basic form that contains the WebBrowser control. The following sections explain how to create and debug these objects.
From the Project menu, select References and add a reference to the Microsoft Internet Controls by checking the appropriate box. The WebBrowser control icon, which looks like a globe, should appear in your toolbox. If this icon does not appear, from the Project menu, select Components and check the box for Microsoft Internet Controls on the Controls tab.
Follow these steps to create your project's class module:
- From the Project menu, select Add Class Module, and then click Open on the New tab of the Add Class Module dialog box.
By default, the Name property of your new class is set to Class1. You can enter another name in the Properties window for your class module. The class in this tutorial is named clsForward because members of the class forward events from document objects in the WebBrowser control to the host application.
- In the General Declarations section of your class module, use the Dim keyword to declare three variables with local (module) scope, as follows:
Option Explicit Dim oObject as Object Dim sMethod as String Dim bInstantiated as Boolean
The purpose of each variable becomes clear as you implement the next several steps.Note For more information about the Option Explicit statement, see the Visual Basic documentation.
- Select Class from the drop-down list on the left side of your class module's code window. An Initialize procedure stub is created automatically. Use the Initialize procedure to set the value of the Boolean variable to False.
Public Sub Class_Initialize() bInstantiated = False End Sub
The Initialize procedure is called when a new member of the class is created. The Boolean variable bInstantiated is a flag. When a new member of the class is instantiated, code in the instantiating procedure changes the value of bInstantiated to True.
- Add a procedure to your class module by selecting Add Procedure from the Tools menu. Select the radio buttons for Sub and Public in the Type and Scope frames, respectively, of the Add Procedure dialog box, and enter a name for your procedure in the Name field. In the following example, the procedure is named Set_Destination.
Public Sub Set_Destination(oInObject as Object, sInMethod as String) Set oObject = oInObject sMethod = sInMethod bInstantiated = True End Sub
This procedure instantiates a new member of the class. Notice that this is where you change the value of the Boolean variable bInstantiated to True. The first parameter to this procedure is the object that handles events trapped by members of the class. The second parameter represents the procedure that handles the trapped event—the "destination" procedure.
- Add one more public subprocedure to your class module. This is where the CallByName function binds the destination procedure at run time. In the next step, you'll make this procedure the default method of your class.
Public Sub My_Default_Method() If bInstantiated Then CallByName oObject, sMethod, vbMethod End if End Sub
It is left to you to write an error-handling procedure to deal with the case where a member of the class was not previously instantiated (bInstantiated is False).
- Follow these steps to identify the procedure just added as the default method of your class. This enables you to invoke the method without explicitly calling it.
- Open the Object Browser either by selecting it from the View menu or by pressing the F2 key.
- Click your project's name in the Object Browser's upper drop-down list.
- Click your class module's name in the Classes list on the left side of the Object Browser. The members of your class are displayed in the right side of the Object Browser.
- Right-click the method of your class module that you want to be your default method, and select Properties from the context menu.
- Click the Advanced button on the Procedure Attributes dialog box. From the drop-down list of Procedure IDs, select Default and then click OK.
Follow these steps to create your project's user interface and your form's procedure code:
- Place a WebBrowser control on your form. This control represents your project's entire user interface. In the Properties window for your control, the default Name property is WebBrowser1. The Name property of the WebBrowser control in this tutorial is set to wbMyBrowser.
- In the code window for your form, enter the following code for your form's Load event.
Private Sub Form_Load() 'Download blank page wbMyBrowser.Navigate2 "about:blank" End Sub
This procedure uses the WebBrowser control's Navigate2 method to download a blank HTML page.
- Add a public subprocedure to your form. This is the destination procedure that handles events in the WebBrowser control. Members of your class forward events from a document object in the WebBrowser control to this procedure.
Public Sub Some_Procedure() MsgBox "Some_Procedure was called." End Sub
This simple procedure displays a message box to inform you that it was called successfully.
- Select your WebBrowser control's name from the left drop-down list in the code window for your form. Then select the DocumentComplete event from the right drop-down list. (You can delete the code stub for the StatusTextChange event that was created automatically.) The WebBrowser control fires the DocumentComplete event when it finishes downloading a Web page (in this case, about:blank).
Private Sub wbMyBrowser_DocumentComplete(ByVal pDisp as Object, URL As Variant) 'Declare new member of class Dim cfForward As clsForward 'Create HTML document inside WebBrowser control Dim sHTML As String sHTML = "<P>This is some text.</P>" sHTML = sHTML & "<P>And here is a button.</P>" sHTML = sHTML & "<BUTTON ID=btnMyButton>" sHTML = sHTML & "Click this button.</BUTTON>" wbMyBrowser.Document.body.innerHTML = sHTML 'Instantiate new member of class Set cfForward = New clsForward cfForward.Set_Destination Me, "Some_Procedure" wbMyBrowser.Document.All("btnMyButton").onclick = cfForward End Sub
After you declare a local variable of your class type (the class is named clsForward in the example), use the New keyword to create a new member of your class. The name of the public subprocedure you created in the previous step (named Some_Procedure in the example) is the second parameter in a call to the procedure that instantiates the new member of your class (named Set_Destination in the example). Some_Procedure is the destination procedure that handles the trapped event. Then you assign the onclick event of all the elements with a given ID property that are contained by a document object in your WebBrowser control to the default method of the new member of your class.
Using the innerHTML property of the BODY object, your program creates an HTML document inside the WebBrowser control.
Now that your code is complete, set a break point in the first executable line of your form's DocumentComplete event to begin debugging the project. Then compile your project by selecting Start With Full Compile from the Run menu, or by pressing the CTRL and F5 keys simultaneously.
When the WebBrowser control finishes loading about:blank, your code executes as follows:
- The DocumentComplete procedure calls the Initialize procedure of the class module to create a new member of the class.
- The Set and New keywords are used to instantiate a new member of the class. Your form (Me) is sent as the first parameter to the Set_Destination procedure in your class module. The form is the object that receives forwarded events. The name of the function is the second parameter to the Set_Destination procedure, so this is the procedure that executes when events are forwarded to your form. The name of the function is in quotation marks because it is sent as a string, a syntax requirement of the CallByName function. The last line of code in your form's Load event establishes the event to be forwarded—in this example, the onclick event of the button element named btnMyButton.
When the new member of your class is instantiated in your form's DocumentComplete event, values are set in the class module for the object, string, and Boolean variables. When you click the button, all that's left is for the default method of your class to forward the event to the destination procedure specified in your form.
This tutorial shows you how to trap and handle events in a Web page you created yourself. You have the luxury of knowing in advance what elements are on the page. And you know the events these elements can fire.
You can also trap events in random Web pages, where you do not know what elements they contain in advance. When you develop applications that trap and handle events on random Web pages, keep the following in mind:
- The WebBrowser control fires a DocumentComplete event for each frame element in a frameSet element.
- The all collection returns a collection if there is more than one element with the same ID property. Use all when you are sure that only one element will be returned.
- Because WebBrowser objects are ActiveX objects, the WebBrowser objects can host objects other than HTML documents. Check for the type of document you are hosting.
You are not limited to trapping the onclick event of a button element. There are many other DHTML Events you can trap, and for many other elements. You can trap the onmouseover event for a elements, for example. You can trap events that bubble to the body element. And your destination procedures can do more than display a simple message box.