Al is the author of Developing ActiveX Web Controls and the forthcoming Developing ActiveX Controls with Visual Basic 5 (both from the Coriolis Group). You can reach Al on the Web at http://www.al-williams. com/awc/.
I invented the World Wide Web. Well, not really. But I might have, and a lot of other people might have, too. It's just that we didn't. Tim Berners-Lee did. Let me explain. Years ago (when DOS was king), my company created two products in response to demands from our clients. One was E-book, a hypertext document system. It allowed text formatting and hyperlinks between documents. Another product we sold in those days was FORMZ, a form processor. You could define fields and choices and submit the form to a script for processing. Gee, kind of sounds like a web browser doesn't it?
There were at least two things that prevented Al Williams Inc. from taking the world by storm: First, I didn't merge the products into one. Besides that, I never saw the utility of making E-book and FORMZ network aware to the point that the documents and scripts could reside on a remote host. Stupid me. Of course, many other companies had similar products. Microsoft had WinHelp that it tried to convert into a multimedia authoring language -- again, not really network aware. There are countless others.
If you think about it, what was revolutionary about Tim Berners-Lee's invention? The technology? No, many people had been writing hypertext and similar programs for a long time. A breakthrough in data representation? No, HTML is really a form of SGML. Tim Berners-Lee's web revolution was because of an idea. An idea to juxtapose two well-understood technologies: hypertext and networking. Amazing.
Many great ideas are just that simple: putting things together in a way no one has ever thought of before. Look at the Weed Eater. If it had a blade on it, anyone could have thought of it (and many people did). No one would think of fishing line as a high-tech innovation. Someone put them together and changed the way you cut your lawn.
Isaac Newton once said, "If I have seen further, it is by standing upon the shoulders of Giants." Sir Isaac would appreciate component software. Imagine if my E-book and FORMZ programs had been components. Maybe I'd have put them together. Perhaps I'd have added a networking component, too. Even better, maybe one of my clients would have made the connection that I failed to make.
This month I'll show you how you can reuse existing ActiveX components to make entirely new ActiveX components with Visual Basic 5. Since I've been talking about web browsers, I'll make one that you can drag and drop into any ActiveX container. Of course, I don't want to write all the code to parse HTML, encode form data, and render pictures. Instead, I'll stand on Microsoft's shoulders and use the Internet Explorer (IE) web browser control.
Why a New Control?
If IE provides a browser control, why should you write another one? Primarily because the IE control doesn't provide a user interface. Sometimes this is what you want -- just a blank window full of HTML. Many times, however, you'd like to have a complete browser with controls and the other accouterments you expect on a browser. Besides, perhaps you'll merge the browser with some other innocuous component and get rich and famous!
Of course, VB5 already has a browser form, but you can only include that as part of another VB program. With a true control, you can use it in any ActiveX container.
If you read the ActiveX SDK documentation, you might think Microsoft has a way for you to use a full-featured browser already. That's not really true. The SDK talks about the two browser objects: InternetExplorer and WebBrowser. The InternetExplorer object has a full interface. It is also a complete running instance of IE. InternetExplorer isn't a control; it is an OLE automation interface that you can use to control a copy of IE.
The WebBrowser control is a proper component, but it lacks any user interface. If you want to see the difference, open up VB5 and start a standard EXE project. From the Project menu, select Components. Then on the Controls tab, select Microsoft Internet Controls, and press OK. Notice the new icon on the tool palette? That is the WebBrowser control. For now, open the Form_Load event handler, add Example 1(a), and run the program. Not exactly the results you'd like, right? Now try dragging a WebBrowser control on your main form. Then replace the Form_ Load event code with Example 1(b).
That's better (assuming you have a connection to the Internet active; otherwise, you might want to specify a local URL). The only problem is that there are no controls to operate the browser. Sure, you can drag buttons on to the form and use them to trigger methods in the WebBrowser control. However, I want a true component that has all the necessary controls ready to go.
Getting Started
The first step to creating a new control that encapsulates the WebBrowser control is to plan it! Figure 1 shows the general appearance I wanted for the control. Notice the graphical buttons -- the component should encapsulate these and not use external files that the final developer will have to carry around.
For this control, I decided to expose several members of the WebBrowser and UserControl objects (see Table 1). The only unique member is ShowStatus, a flag that determines if the browser shows its status bar or not.
Armed with a plan, it is trivial to start a new ActiveX Control project. This causes VB to create a blank UserControl object which is similar to a form, but is really an ActiveX control. You drag buttons, static text, and text boxes on to the UserControl just as you would any ordinary VB form. To get the WebBrowser control to appear you'll have to add it using Tools/Components again (since this is a new project). The WebBrowser control may not draw correctly at design time, but don't worry about it.
The browser needs to recalculate the layout of all the internal controls when the main control resizes. Therefore, you don't need to worry about the design-time layout (see Figure 2). After all the code is in place, the internal components will move to the correct places when the main control starts (and every time it resizes, too).
Defining Members
Defining members in VB5 is simple. Just use the ActiveX Interface Wizard to specify the members and connect them to the underlying components (see Figures 3, 4, and 5). In the case of the ShowStatus property, you'll need to manually define it, even though it corresponds closely to the URL.Visible property. You can do that on the final screen of the Wizard (see Figure 6).
You would think that selecting default property names in the Wizard would cause the Wizard to assign the correct dispatch IDs for these properties. However, it doesn't. You'll need to go to the Procedure Attributes menu item (on the Tools menu), press the Advanced button, and change the Procedure ID field to match the names of the standard properties (see Figure 7). While you are at it, you can set the property categories, if you like.
Another annoyance is that the Wizard doesn't define certain members to be the correct type. Look at the properties for a form. Notice the BorderStyle property, in particular. It has six possible values (0-5). However, the property editor shows the values with descriptive tags (like Sizable, for example). It does this because VB defines an Enum for a special type used only with BorderStyle. However, the Wizard defines our BorderStyle property as an Integer. Therefore, the property editor doesn't show the descriptive tags for our BorderStyle.
I decided not to worry too much about this, since I think the Wizard will work properly when VB5 is released. However, if you can find the name of the enumeration, just patch up the code to treat the property as the correct type. Most (but not all) of the types are visible in the object browser (look in the View menu). If you prefer, you can define your own enumeration. Look at the ShowStatus property in Listing One to see how that works. Example 2 is the Enum for that property.
Since the ShowStatus property uses the BrStatusType, the property browser works as you'd expect. For those that don't work right, the end programmer simply has to look up the numeric value in Help and plug it in directly. Remember, this is a beta copy of VB5, and I strongly suspect the release version won't have this problem.
Making it Work
Making the control work is perhaps the easiest part of the whole process. The buttons all correspond to simple methods in the WebBrowser control (see Table 2 and Listing One). The only odd things to implement are the URL entry bar near the top, the logo that changes color to indicate progress, and the status bar. I'll show you how the logo works after we talk about resources. The other interface elements are trivial.
The URL bar examines keystrokes using the Url_KeyPress event handler. If the key is equal to 13 (the Enter key), the code calls the WebBrowser.Navigate method, passing the contents of the URL bar. It also sets the key to 0 so that the control doesn't generate a beep to indicate that it doesn't know what to do with the key.
The status bar requires a bit more work. The WebBrowser control generates StatusTextChange events when it has updated status text. Sometimes this text is empty, so the event handler in the component ignores empty text. This always keeps the last message in the status bar, which is often useful. Otherwise, the code just copies the text passed with the event into the status bar's caption.
Using Resources
How can you handle the pictures used for the buttons and the progress logo? It would be a bad idea to require the component's users to have bitmap files. Instead, you want to build the bitmaps into the control. You can do this with resources.
Resources are nothing more than binary data attached to a program file (in this case, an ActiveX control in a DLL). Resources can be icons, bitmaps, dialog boxes, strings, or even user-defined data. In this case, you want to store bitmaps.
The first step to adding a resource is to build an RC file using a text editor. The RC file in this case is quite simple (see Listing Two). Each line defines a relationship between a number and an ordinary bitmap file. You'll use the number in your program to retrieve the picture as a Picture object.
Once you have the RC file completed, you have to run RC (a Microsoft program) to create a RES file. Just name the RC file on RC's command line; RC PICS.RC, for example, creates PICS.RES. The RES file contains a binary representation of the RC file and all of the data in each BMP file, as well. You add the RES file to your control project using Add File on the Project menu. Now your project contains the bitmaps (and any other resources mentioned in the RC file).
How do you access the pictures? Simply use LoadResPicture. You'll need to pass it the number of the image and vbResBitmap to indicate that you want a bitmap. The vbResBitmap constant is necessary because it is possible to have an icon, for example, that uses the same number as a bitmap. This call returns a Picture object that you can use just like any other picture object. Listing One loads all the pictures into the buttons (and the logo control) during the UserControl_Initialize event.
There are many other kinds of resources you can add to an RC file (see Table 3). There are also many editors designed to permit you to create and edit RC files graphically. Practically every Windows C++ environment comes with such a tool. Resource editors also are available as separate products.
Of course, you only use LoadResPicture for icons, bitmaps, and cursor images (vbResIcon, vbResBitmap, and vbResCursor, respectively). Use LoadResString to read strings from your resources. You can also use LoadResData to load nearly anything from your resources to a byte array (see Table 4). Of course, in most cases, it isn't very useful to load a bitmap as a byte array.
Notice that the code also resizes the buttons to match the size of the picture. Doing this requires a little understanding about the Width and Height parameters that different components support under VB. By default, forms and user controls always specify units in TWIPS (1/20 of a point or 1/1440 of an inch). Buttons and similar controls use whatever value their container uses. Pictures, however, are different. The Picture object's measurements are always in HIMETRIC units (Windows slang for 1/100 of a millimeter). Although you could convert HIMETRIC to TWIPS, it is easier to change the user control to use millimeters. You can do this by changing the ScaleMode property on the user control itself. Then it is a simple matter to divide the Picture's HIMETRIC values by 100 to obtain millimeter measurements (remember, Height and Width don't have to be integers).
You can find the conversion code in the SetButton subroutine. This simple function reads the bitmap from the resources, and sets the button size to match (after scaling millimeters to HIMETRIC units).
Another use for LoadResPicture is in creating the color-changing logo. When the WebBrowser control fires a ProgressChange event, the component loads a different color bitmap from the resources into the Logo control. When the DownloadComplete event occurs, it resets the logo back to its original state. The code also uses DownloadComplete (along with DownloadBegin) to disable (and enable) the StopCmd button.
Handling Errors
One important facet of creating a new control is properly handling errors. Try an experiment: Find the GoBack method, and comment out the "On Error Resume Next" statement. Then comment out the CommandStateChange event handler. Next, run the sample program (available electronically; see "Programmer's Services, page 3) and immediately press the back button. The result is not very pretty (see Figure 8) and is certainly something an end user should never see.
The "On Error Resume Next" statement prevents the user from seeing the error dialog. Instead, your code has to check the Err object (unless you just want to ignore the error). If Err.Number is zero, no error occurred. Err is a global object. You don't need to create it or worry about it in any way. Just use it.
The Err object has several other members, but only the Description is usually useful in this context. The Description field, as you might guess, contains the text that describes the error ready to display.
In many cases, you just want to ignore the error anyway. If you wanted to be a stickler for details, you could note each error code you expect, and report only errors you don't expect. However, in this case, just beeping in every case is sufficient.
There is another way you can use "On Error" that might be preferable if you have a large procedure with many possible errors. However, it involves the use of the dreaded "GoTo" statement. Instead of using Example 3(a), you can write Example 3(b) to centralize your error processing.
Of course, many people don't like to use GoTo. Use your own judgment, but VB will let you write "On Error GoTo."
Distributing Components
You'll find an example program that uses the browser control online. It is really nothing more than a form that contains the control and resizes it when the user resizes the form. Since I created both projects, I had no trouble using the control. However, if you distribute a control like this one, watch out for legal pitfalls. Of course, your control needs the Visual Basic run-time DLLs. However, in this case, it also needs Internet Explorer. You can just tell your users that they have to get a copy themselves. You can also arrange with Microsoft (or whatever vendor you've used) to distribute the software with your own. Oddly, you can easily get an arrangement with Microsoft to distribute IE, but not everyone will accommodate you.
Other Changes
You might find it instructive to make a few changes to the browser control. One nice feature would be properties that set the pictures to use for each button. If the properties are empty, you could use the original bitmaps. You also might want to try exposing more of the WebBrowser events to allow the container more options when interacting with the browser.
A Final Thought on the Web
Have you ever wondered about the Nobel peace prize? I always wonder about odd things that no one else seems to pay much attention to. For instance, it always bothered me that there is an Absorbine Jr. What the heck is Absorbine Sr.? (It turns out plain Absorbine is horse liniment.)
I recently had a chance to ask a Rabbi why you always see signs in Baskin Robbins stores that say "All of our products are Kosher except for those containing miniature marshmallows." Oddly, no one I've asked at Baskin Robbins over the years seemed to know. There seems to be no end to the amount of odd trivia for me to wonder about. Years ago I heard about the Nobel winners on the news, and it got my curiosity going.
Alfred Nobel, the man who set up the prize, invented dynamite (look it up at http://www.nobel.se/). Poor Alfred, he thought he had assured the destruction of the world, and was quite depressed. He set up his awards to encourage acts of peace to atone for his invention. Of course, in retrospect, dynamite isn't so bad. Truthfully, dynamite can be used for good and ill. Dynamite is neither good nor bad; people are.
The World Wide Web isn't inherently good or bad. There are many interesting, informative sites. There are even more bland, mediocre sites. There are plenty of sites most folks would find offensive. There are even more sites that announce the creator's preference in pizza, movies, and music (as though we care).
When is the last time you saw a web site that you thought was culturally important? Have you seen a site that fosters greater international understanding and world peace? I'm sure there must be some sites like this, but not many.
Perhaps a WWW "Nobel" award would be in order. The Web can be a powerful tool for social change, or a powerful waste of telecommunications bandwidth. As web developers, I suppose it is up to us.
Conclusion
Okay, so I didn't invent the Web. I didn't even write a web browser. Thanks to ActiveX and VB5 I can still pretend that I did. Simple components can combine to create fantastic creations. Just look at Tinkertoys, or UNIX shell programming. Let me know what you've created lately.
DDJ
Listing One
VERSION 5.00Object = "{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}#1.0#0"; "SHDOCVW.DLL" Begin VB.UserControl Browser ClientHeight = 5748 ClientLeft = 0 ClientTop = 0 ClientWidth = 7740 PropertyPages = "browser.ctx":0000 ScaleHeight = 101.388 ScaleMode = 6 'Millimeter ScaleWidth = 136.525 Begin SHDocVwCtl.WebBrowser WebBrowser Height = 4092 Left = 0 TabIndex = 1 Top = 1080 Width = 7452 Object.Height = 341 Object.Width = 621 AutoSize = 0 ViewMode = 1 AutoSizePercentage= 0 AutoArrange = -1 'True NoClientEdge = -1 'True AlignLeft = 0 'False End Begin VB.CommandButton FwdCmd Height = 372 Left = 1800 Style = 1 'Graphical TabIndex = 9 ToolTipText = "Go Forward" Top = 120 Width = 972 End Begin VB.CommandButton BackCmd Height = 372 Left = 840 Style = 1 'Graphical TabIndex = 8 ToolTipText = "Go Back" Top = 120 Width = 972 End Begin VB.CommandButton SearchCmd Height = 372 Left = 6600 Style = 1 'Graphical TabIndex = 7 ToolTipText = "Search" Top = 240 Width = 972 End Begin VB.CommandButton HomeCmd Height = 372 Left = 5400 Style = 1 'Graphical TabIndex = 6 ToolTipText = "Return Home" Top = 240 Width = 972 End Begin VB.CommandButton StopCmd Enabled = 0 'False Height = 372 Left = 4200 Style = 1 'Graphical TabIndex = 5 ToolTipText = "Stop" Top = 240 Width = 972 End Begin VB.CommandButton RefreshCmd Height = 372 Left = 3000 Style = 1 'Graphical TabIndex = 4 ToolTipText = "Refresh" Top = 240 Width = 972 End Begin VB.TextBox Url Height = 288 Left = 0 TabIndex = 2 Top = 840 Width = 7332 End Begin VB.PictureBox Logo AutoSize = -1 'True Height = 816 Left = 0 ScaleHeight = 768 ScaleWidth = 768 TabIndex = 0 Top = 0 Width = 816 End Begin VB.Label Status Caption = "Initializing" Height = 252 Left = 0 TabIndex = 3 Top = 5520 Width = 7452 End End Attribute VB_Name = "Browser" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = True Option Explicit 'Event Declarations: Event TitleChange(ByVal Text As String) 'MappingInfo=WebBrowser,WebBrowser,-1,TitleChange Enum BRStatusType brShowStatus = True brHideStatus = False End Enum </p> Private Sub BackCmd_Click() GoBack End Sub </p> Private Sub FwdCmd_Click() GoForward End Sub </p> Private Sub HomeCmd_Click() GoHome End Sub </p> Private Sub RefreshCmd_Click() On Error Resume Next WebBrowser.Refresh If Err.Number <> 0 Then Beep End Sub </p> Private Sub SearchCmd_Click() GoSearch End Sub </p> Private Sub StopCmd_Click() On Error Resume Next WebBrowser.Stop If Err.Number <> 0 Then Beep End Sub </p> Private Sub Url_KeyPress(KeyAscii As Integer) If KeyAscii = 13 Then On Error Resume Next WebBrowser.Navigate Url.Text If Err.Number <> 0 Then Beep KeyAscii = 0 End If End Sub Private Sub SetButton(b As CommandButton, n As Integer) b.Picture = LoadResPicture(n, vbResBitmap) b.Height = b.Picture.Height / 100 b.Width = b.Picture.Width / 100 End Sub </p> Private Sub UserControl_Initialize() Logo.Picture = LoadResPicture(20, vbResBitmap) SetButton HomeCmd, 10 SetButton StopCmd, 11 SetButton SearchCmd, 12 SetButton RefreshCmd, 13 SetButton BackCmd, 14 SetButton FwdCmd, 15 </p> Url.Text = "http://www.al-williams.com/awc" WebBrowser.Navigate Url.Text End Sub ' When you resize, lots goes on Private Sub UserControl_Resize() Const offset = 1 ' 1 mm margins ' Set Logo to top left corner Logo.Top = 0 Logo.Left = 0 ' Reposition everything relative to Logo RefreshCmd.Top = offset RefreshCmd.Left = ScaleWidth / 2 StopCmd.Top = offset StopCmd.Left = RefreshCmd.Left + RefreshCmd.Width + offset HomeCmd.Top = offset HomeCmd.Left = StopCmd.Left + StopCmd.Width + offset SearchCmd.Top = offset SearchCmd.Left = HomeCmd.Left + HomeCmd.Width + offset BackCmd.Top = offset BackCmd.Left = Logo.Width + offset FwdCmd.Top = offset FwdCmd.Left = BackCmd.Left + BackCmd.Width + offset </p> Url.Top = Logo.Height + offset Url.Left = 0 Url.Width = ScaleWidth WebBrowser.Left = 0 WebBrowser.Top = Url.Height + Logo.Height + offset WebBrowser.Width = ScaleWidth WebBrowser.Height = ScaleHeight - WebBrowser.Top - Status.Height Status.Left = 0 Status.Top = ScaleHeight - Status.Height Status.Width = ScaleWidth End Sub </p> Private Sub WebBrowser_CommandStateChange(ByVal Command As Long, ByVal Enable As Boolean) Select Case Command Case CSC_NAVIGATEFORWARD FwdCmd.Enabled = Enable Case CSC_NAVIGATEBACK BackCmd.Enabled = Enable End Select End Sub </p> Private Sub WebBrowser_DownloadBegin() StopCmd.Enabled = True ' Let user stop End Sub </p> Private Sub WebBrowser_DownloadComplete() ' Reset logo and Disable stop button Logo.Picture = LoadResPicture(20, vbResBitmap) StopCmd.Enabled = False End Sub </p> Private Sub WebBrowser_ProgressChange(ByVal Progress As Long, ByVal ProgressMax As Long) Static Logonum As Integer ' As progress is made, change logo image Logonum = Logonum + 1 If Logonum = 3 Then Logonum = 0 Logo.Picture = LoadResPicture(20 + Logonum, vbResBitmap) End Sub </p> Private Sub WebBrowser_StatusTextChange(ByVal Text As String) If Text <> "" Then Status.Caption = Text End Sub 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=UserControl,UserControl,-1,BackColor Public Property Get BackColor() As OLE_COLOR Attribute BackColor.VB_Description = "Returns/sets the background color used to display text and graphics in an object." Attribute BackColor.VB_ProcData.VB_Invoke_Property = ";Appearance" Attribute BackColor.VB_UserMemId = -501 BackColor = UserControl.BackColor End Property </p> Public Property Let BackColor(ByVal New_BackColor As OLE_COLOR) UserControl.BackColor() = New_BackColor PropertyChanged "BackColor" End Property </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=UserControl,UserControl,-1,ForeColor Public Property Get ForeColor() As OLE_COLOR Attribute ForeColor.VB_Description = "Returns/sets the foreground color used to display text and graphics in an object." Attribute ForeColor.VB_ProcData.VB_Invoke_Property = ";Appearance" Attribute ForeColor.VB_UserMemId = -513 ForeColor = UserControl.ForeColor End Property </p> Public Property Let ForeColor(ByVal New_ForeColor As OLE_COLOR) UserControl.ForeColor() = New_ForeColor PropertyChanged "ForeColor" End Property </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=UserControl,UserControl,-1,Enabled Public Property Get Enabled() As Boolean Attribute Enabled.VB_Description = "Returns/sets a value that determines whether an object can respond to user-generated events." Attribute Enabled.VB_ProcData.VB_Invoke_Property = ";Behavior" Attribute Enabled.VB_UserMemId = -514 Enabled = UserControl.Enabled End Property </p> Public Property Let Enabled(ByVal New_Enabled As Boolean) UserControl.Enabled() = New_Enabled PropertyChanged "Enabled" End Property </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=UserControl,UserControl,-1,BackStyle Public Property Get BackStyle() As Integer Attribute BackStyle.VB_Description = "Indicates whether a Label or the background of a Shape is transparent or opaque." Attribute BackStyle.VB_ProcData.VB_Invoke_Property = ";Appearance" Attribute BackStyle.VB_UserMemId = -502 BackStyle = UserControl.BackStyle End Property </p> Public Property Let BackStyle(ByVal New_BackStyle As Integer) UserControl.BackStyle() = New_BackStyle PropertyChanged "BackStyle" End Property </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=UserControl,UserControl,-1,BorderStyle Public Property Get BorderStyle() As Integer Attribute BorderStyle.VB_Description = "Returns/sets the border style for an object." Attribute BorderStyle.VB_ProcData.VB_Invoke_Property = ";Appearance" Attribute BorderStyle.VB_UserMemId = -504 BorderStyle = UserControl.BorderStyle End Property </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=UserControl,UserControl,-1,Refresh Public Sub Refresh() Attribute Refresh.VB_Description = "Forces a complete repaint of a object." Attribute Refresh.VB_UserMemId = -550 UserControl.Refresh End Sub </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=WebBrowser,WebBrowser,-1,GoSearch Public Sub GoSearch() Attribute GoSearch.VB_Description = "Go Search Page." On Error Resume Next WebBrowser.GoSearch If Err.Number <> 0 Then Beep End Sub </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=WebBrowser,WebBrowser,-1,GoHome Public Sub GoHome() Attribute GoHome.VB_Description = "Go home/start page." On Error Resume Next WebBrowser.GoHome If Err.Number <> 0 Then Beep End Sub </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=WebBrowser,WebBrowser,-1,GoForward Public Sub GoForward() Attribute GoForward.VB_Description = "Navigates to the next item in the history list." On Error Resume Next WebBrowser.GoForward If Err.Number <> 0 Then Beep End Sub </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=WebBrowser,WebBrowser,-1,GoBack Public Sub GoBack() Attribute GoBack.VB_Description = "Navigates to the previous item in the history list." On Error Resume Next WebBrowser.GoBack If Err.Number <> 0 Then Beep End Sub </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=WebBrowser,WebBrowser,-1,LocationURL Public Property Get LocationURL() As String Attribute LocationURL.VB_Description = "Gets the full URL/path currently viewed." LocationURL = WebBrowser.LocationURL End Property </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=WebBrowser,WebBrowser,-1,LocationName Public Property Get LocationName() As String Attribute LocationName.VB_Description = "Gets the short (UI-friendly) name of the URL/file currently viewed." LocationName = WebBrowser.LocationName End Property </p> 'WARNING! DO NOT REMOVE OR MODIFY THE FOLLOWING COMMENTED LINES! 'MappingInfo=WebBrowser,WebBrowser,-1,Navigate Public Sub Navigate(Url As String, Optional Flags As Variant, Optional TargetFrameName As Variant, Optional PostData As Variant, Optional Headers As Variant) Attribute Navigate.VB_Description = "Navigates to a URL or file." On Error Resume Next WebBrowser.Navigate Url, Flags, TargetFrameName, PostData, Headers If Err.Number <> 0 Then Beep End Sub </p> Public Property Get ShowStatus() As BRStatusType Attribute ShowStatus.VB_Description = "Set to True if browser should show status messages" Attribute ShowStatus.VB_ProcData.VB_Invoke_Property = ";Appearance" ShowStatus = Status.Visible End Property </p> Public Property Let ShowStatus(ByVal New_ShowStatus As BRStatusType) Status.Visible = New_ShowStatus PropertyChanged "ShowStatus" End Property </p> Private Sub WebBrowser_TitleChange(ByVal Text As String) RaiseEvent TitleChange(Text) End Sub </p> 'Initialize Properties for User Control Private Sub UserControl_InitProperties() End Sub </p> 'Load property values from storage Private Sub UserControl_ReadProperties(PropBag As PropertyBag) </p> UserControl.BackColor = PropBag.ReadProperty("BackColor", &H80000005) UserControl.ForeColor = PropBag.ReadProperty("ForeColor", &H80000008) UserControl.Enabled = PropBag.ReadProperty("Enabled", True) UserControl.BackStyle = PropBag.ReadProperty("BackStyle", 1) Status.Visible = PropBag.ReadProperty("ShowStatus", True) End Sub </p> 'Write property values to storage Private Sub UserControl_WriteProperties(PropBag As PropertyBag) </p> Call PropBag.WriteProperty("BackColor", UserControl.BackColor, &H80000005) Call PropBag.WriteProperty("ForeColor", UserControl.ForeColor, &H80000008) Call PropBag.WriteProperty("Enabled", UserControl.Enabled, True) Call PropBag.WriteProperty("BackStyle", UserControl.BackStyle, 1) Call PropBag.WriteProperty("ShowStatus", Status.Visible, True) End Sub
Listing Two
10 BITMAP home.bmp11 BITMAP stop.bmp 12 BITMAP search.bmp 13 BITMAP refresh.bmp 14 BITMAP lhand.bmp 15 BITMAP rhand.bmp 20 BITMAP logor.bmp 21 BITMAP logog.bmp 22 BITMAP logob.bmp </p>