Integrate the Webbrowser Control in Visual Foxpro
The browser control lets you embed the Internet Explorer directly into your own Visual FoxPro forms as opposed to an external desktop window. Also the events that IE publishes are exposed as native events of the ActiveX control, so you don't need to implement an interface to handle events. Which one of these components you use is really up to your application scenario, but in general I find that I have more control over the lifetime of the control if I use the ActiveX control and more options in how I can mix VFP and Web browser content.
To use the Web Browser control on a Fox form, use the following steps:
- Open a new FoxPro Form
- Use the ActiveX control picker and drop Microsoft Web Browser Control onto the form
- Name the control oBrowser
- Set the Refresh Event code to NODEFAULT
The last step is necessary as the Refresh event causes an error in VFP to occur.

Fig. 1: The Web Browser control on a FoxPro form in design mode. Once on the form the control can be fully automated via code, and events handled by simple event methods. This example demonstrates several features of the control.
When first run without a URL the control shows an empty surface and an empty HTML document and THISFORM.oBrowser.Document = null. You can use the Navigate() method to then navigate to a specific URL:
THISFORM.oBrowser.Navigate("http://www.west-wind.com")
If you end up modifying the HTML document in your code or if you want to display an emty document you'll always want to make sure load at least an empty document with:
THISFORM.oBrowser.Navigate("about:blank")
about:blank is an HTML document that is valid, but contains only
and you can then continue to modify the document. For example:
THISFORM.oBrowser.Document.Body.InnerHtml ="
Hello World
"
Alternately, your Init() of the form can already navigate to a specific URL or file. Remember as with the IE Application object you need to make sure that the document is loaded completely before you start modifying any part of it via code. Use WaitForReadyState() to do this as necessary.
In this case the form can be passed a URL parameter which looks like this in the form's Init():
* WebBrowser.Init()
LPARAMETERS lcUrl
IF !EMPTY(lcUrl)
THISFORM.Navigate(lcUrl)
ELSE
THISFORM.Navigate("http://www.west-wind.com/")
ENDIF
THISFORM.Resize()
The Resize of the form resizes the Web Browser control to fit the form.

Fig. 2: The Web Browser control once navigated displays a full IE client frame inside of the FoxPro form.
As you can see from the code above the form includes a Navigate() method which in turn calls the browser's Navigate method. Although not essential in this application, in most application contexts this is a good idea to properly wrap the calls. For example, trimming the URL properly of spaces, or performing any fixups of the URL before passing it to the navigate method. In this form the Navigate method deals with checking what the current URL is and only navigating to it if the URL has changed.
* Navigate Method of the form
LPARAMETERS lcUrl
IF EMPTY(lcUrl)
lcUrl = TRIM(THISFORM.txtUrl.DisplayValue)
ENDIF
*** Don't navigate if we're on the same URL
IF lcUrl = THISFORM.cCurrentUrl
RETURN
ENDIF
IF !EMPTY(lcUrl)
THISFORM.oBrowser.Navigate(lcUrl)
ELSE
THISFORM.oBrowser.Navigate("http://www.west-wind.com/")
ENDIF
IF EMPTY(lcUrl)
thisform.cCurrentUrl = "asasdasd"
ELSE
THISFORM.cCurrentUrl = lcUrl
ENDIF
THISFORM.Resize()
Listing 1: The Navigate method of the form keeps track of where we are
The form also captures a URL history in the combo box by using the NavigateComplete2 event which fires when the page is done loading. When the form is shutdown, the list is then written into a DBF file which is reloaded if it exists so you have a history that persists.
*** NavigateComplete2 Event ***
LPARAMETERS pdisp, url
IF EMPTY(Url)
RETURN
ENDIF
thisform.txtUrl.AddItem(Url,1)
THISFORM.txtUrl.Value = Url
*** Drop off the last item if list is too large
IF thisform.txtUrl.ListCount > 25
thisform.txtUrl.RemoveItem(26)
ENDIF
Listing 2: Adding the last URL to the combobox
The Web Browser control also includes native methods for things like GoBack(), GoForward(), GoSearch (), GoHome() that you are familiar with in the browser's user interface.
It's not just for Web Content
The Web browser control has many uses that go way beyond the Web. HTML is not a good tool for everything, but it's nice for certain applications that require a rich graphical experience. Sometimes it's just easier to display information as HTML—sometimes it's a good idea to build output dynamically with VFP code into HTML format and use the browser control as the preview mechanism to see what it will actually look like. Figure 3 shows a VFP Desktop application for building HTML Help Files which uses local HTML content dynamically generated to disk to display a preview of the final HTML topic layout in the Web Browser control.

Fig. 3: Displaying HTML content doesn't have to come from the Web. In this application the HTML is generated dynamically to disk and previewed from a file for each topic displayed. The rich display of HTML provides interface options that are difficult to achieve with VFP forms.
The concept behind the code for this kind of interface is pretty straight forward. The form contains a Web Browser control, which is refreshed based on an action in the VFP user interface. In this case if a user clicks on one of the topics, the data that is stored in the particular Help Interface record is rendered into HTML into a static file called _wwHelp_Preview.htm. The Web Browser control is then Navigated to this page to display the content as HTML in the control.
The high level code, triggered by the treeview's NodeClick event is pretty simple and contained in a method called GoTopic():
lcHTML = oHelp.RenderTopic() && Create the HTML output
STRTOFILE(lcHTML,this.oHelp.cProjectPath + "_preview.htm")
THISFORM.oBrowser.Navigate("file://" + this.oHelp.ProjectPath + "_preview.htm")
The Help Builder application is an extreme example for HTML content, because HTML Help is, well, all about HTML. But this sort of interface comes in handy in many situations where you want to display data dynamically. For example, I frequently use HTML lists in applications to display line item lists where the line items have varying numbers of fields to display. Take a look at Figure 4 which shows a list of line items with an optional discount. This would be difficult to display using standard VFP controls.

Fig. 4: Using HTML inside of standard VFP forms can also provide a clean, modern look as well as help with varying data such as the discount above. HTML also works well for lists displaying memo data which is nearly impossible with standard VFP controls.
Any kind of output that needs to stream and has varying sizes per row – memo fields for example – are also good candidates for HTML display. Nested relations (such as many-to-many) are also easier to handle with a streamable output source like HTML, rather than a fixed-field-based format like input fields and grids.
Capturing Navigation Events
This brings up another point that is fairly important if you plan to integrate the Web Browser control. How do you capture clicks on a form and let VFP handle these events? The key to this task is the BeforeNavigate2 event of the Web Browser control. This event fires before the browser navigates to the requested page when you click on any link and lets you trap the URL that you are accessing. It gives you a hook to check the URL and if necessary perform actions in your VFP code to act in response of the URL.
For stand-alone applications it's even more important to capture navigation events. Rather than going out to the Internet, applications typically must perform operations before displaying data. For example, in the invoice form above I want to capture an item link the user clicks on, and then bring up the appropriate line item form.
The trick to this feat is to use a special syntax for 'application' URLs that are structured as directives. In order for a URL to be valid it must have a protocol, followed by a path. Links in the line item list above look like this:
VFPS://EDITITEM/10121
All Web browser links in the application start with a VFPS:// (VFP Script) protocols, and have an EventId (EDITITEM) and optional parameters that are passed. There's nothing special about the VFPS:// protocol prefix – it's a name I made up, but can be anything as long as it has the :// at the end. The EventID and parameters are then parsed in the BeforeNavigate2 method and call the appropriate forms or functions. Here's the code for BeforeNavigate2() that fires the EditItem() method on the form:
* BeforeNavigate2 event handler
LPARAMETERS pdisp, lcUrl, flags, targetframename, ;
postdata, headers, cancel
LOCAL lnPK, lcCommand
DO CASE
CASE LEFT(UPPER(lcUrl),7) = "VFPS://"
lcCommand = UPPER(STREXTRACT(lcUrl,"//","/"))
DO CASE
CASE lcCommand = "EDITITEM"
lnPK = VAL(SUBSTR(lcUrl,RAT("/",lcUrl)+1))
THISFORM.EditItem(lnPK)
CANCEL = .T. && don't want to actually show lcUrl
CASE lcCommand = "DELETEITEM"
lnPK = VAL(SUBSTR(lcUrl,RAT("/",lcUrl)+1))
THISFORM.RemoveLineItem(lnPK)
CANCEL = .T. && don't want to actually show lcUrl
ENDCASE
ENDCASE
Listing 3: Handling application Urls prefixed with the VFPS:// protocol
The key here is to keep these 'application' URLs simple so you don't have to do extensive parsing. Here only a PK is passed as a parameter. In each case the navigation is cancelled by setting the CANCEL flag to .T. This is important as you don't want to update the Web Browser control with new data. Instead you want to pop up a new FoxPro form to display data – the initial display of the Web Browser control is only changed when the invoice is refreshed (ie. An item was added or deleted or a new invoice was selected altogether).