View RSS Feed

Development Team Blog

Getting More Out of Your Browser Control

Rating: 7 votes, 5.00 average.
Since the introduction of COM in Visual DataFlex, around Visual DataFlex version 5, there has been a example application that contains an ActiveX object that represents Microsoft's Internet Explorer. In the example, that loads a default web-page, you can enter another address, click on a hyper-link which loads a new page, go back or forward, all the usual operations you can do with Internet Explorer itself too. While this example is good you can do much more and this blog is informing you about that, encouraging you to take advantage.

Tip of the Day
If you have installed Visual DataFlex 15.0 or higher (and why not) you must have seen the "Did You Know" dialog of Database Explorer.

The dialog is a standard ModalDialog object with 2 buttons BUT with a Internet Explorer browser control in it which shows the light-bulb picture, the text "Did You Know?" and a tip text itself. The reason to mention this dialog in this blog is that the control over the contents of the object is fully done in memory and it requires no HTML page on disk that needs to be loaded, which is a requirement of the cWebBrowser control class.

If you look for a method to display dynamic content in the cWebBrowser class you will not find one since it simply does not exist. It is not a method we left out when the class was generated but it is not present in the control itself. So how can Database Explorer do this?

Generate Code
To get access to that level of access in Internet Explorer you need to generate COM classes from another library that Microsoft installed on your computer. It is the MSHTML type library. In the Import ActiveX dialog in Visual DataFlex Studio

it is named "Microsoft HTML Object Library". If you generate the package of this library you get a stunning 268,625 lines of Visual DataFlex source code! Ehh, did you want to write a small program? Forget it... or not? Yes it is or not because you do not need all the code. For a lot of your work you only need a couple of classes and not even all the methods in those classes.

In fact if your goal is to "just" display dynamically built HTML you only need one class and 5 methods from that class. You need the cComHTMLDocument class. So find this class in the huge cComScriptlet.pkg and copy it into a new package which you want to use.

Code:
// CoClass
// ProgID: htmlfile
// CLSID: {25336920-03F9-11CF-8FD0-00AA00686F13}
Class cComHTMLDocument is a cComAutomationObject
    Procedure Construct_Object
        Forward Send Construct_Object
        Set psProgID to "{25336920-03F9-11CF-8FD0-00AA00686F13}"
        Set psEventId to "{3050F260-98B5-11CF-BB82-00AA00BDCE0B}"
        Set peAutoCreate to acNoAutoCreate
    End_Procedure
End_Class
Note that I left off the usual Import_Class_Protocol statements for COM objects being
Code:
Import_Class_Protocol cComDispHTMLDocument
Import_Class_Protocol cComHTMLDocumentEvents
I did this because we only need a couple of methods from the cComDispHTMLDocument class and none of the methods of the cComHTMLDocumentEvents class. All the methods we need we copy from the cComDispHTMLDocument class directly into our class. This way we can keep the code small and compact.

Building Up
As this blog is about extending your browser control ActiveX you should already have an object of the cComWebBrowser class in your component (e.g. dbView). Make sure a default page is loaded. This does not have to be a real page on disk or from a URL but can be the blank page known as "about:blank". So the code could be something like:

Code:
Object oDHTML is a cComWebBrowser
    Set Size to 308 569
    Set Location to 5 5
    Set peAnchors to anAll

    // Initialize the browser with an empty page
    Procedure OnCreate
        Send ComNavigate "about:blank" 0 0 0 0
    End_Procedure
End_Object
We need to add an object of the cComHTMLDocument class to the oDHTML object. Technically the Visual DataFlex proxy object does not need to be a child object of oDHTML but in this case we simply put it inside the object.
Code:
Object oDHTML is a cComWebBrowser
    Set Size to 308 569
    Set Location to 5 5
    Set peAnchors to anAll

    // Object that helps us displaying the dynamic content
    Object oHTMLDocument is a cComHTMLDocument
    End_Object

    // Initialize the browser with an empty page
    Procedure OnCreate
        Send ComNavigate "about:blank" 0 0 0 0
    End_Procedure
End_Object
The change is shown in a red color.
The oHTMLDocument is of course our access point to get the dynamic contents shown. Before this object can be used its COM counter part needs to be created and needs to be connected to the oDHTML object. You do this by coding:

Code:
Variant vDocument

Get ComDocument to vDocument
Set pvComObject of oHTMLDocument to vDocument
inside a method like OnCreate, Activating or a self named method of the oDHTML object.
To be able to get our information shown the documentation says we need to open the document. You do this with:

Code:
Get ComOpen of oHTMLDocument "text/html" "replace" Nothing True to vDocument
Set pvComObject of oHTMLDocument to vDocument
To wrap it up the code is now:
Code:
Object oDHTML is a cComWebBrowser
    Set Size to 308 569
    Set Location to 5 5
    Set peAnchors to anAll

    // Object that helps us displaying the dynamic content
    Object oHTMLDocument is a cComHTMLDocument
    End_Object

    Procedure ConnectItAll
        Variant vDocument

        Get ComDocument to vDocument
        Set pvComObject of oHTMLDocument to vDocument

        Get ComOpen of oHTMLDocument "text/html" "replace" Nothing True to vDocument
        Set pvComObject of oHTMLDocument to vDocument
    End_Procedure

    // Initialize the browser with an empty page
    Procedure OnCreate
        Send ComNavigate "about:blank" 0 0 0 0
    End_Procedure
End_Object
The change is shown in a red color.

HTML
The basics of HTML are very simple. You need to have an openings tag and a closing tag. Inside these outer tags you need at minimum a element. Inside this inner tag we can place the information to be shown. For example:
HTML Code:
This is a test
In Visual DataFlex source code this can be:
Code:
Procedure DisplayTest
    String sHTML

    Move 'This is a test
' to sHTML
End_Procedure
Show HTML
Now how do we get this text into the control? You need to use the Write and Close methods. The Close is very simple, the Write is more complex. The Write message takes a variant array as argument. Sounds simple... or not? Indeed it is not simple because it needs to be an array of BSTR characters. This means that we need to convert the normal Visual DataFlex OEM string into a UNICODE string. We do this with the following method:
Code:
// Function : StrToWStr
// Purpose  : Converts a OEM string to a Unicode string
Function StrToWStr Global String sData Returns String
    Integer iLength iResult
    String sBuffer

    // Get size of buffer
    Move (MultiByteToWideChar(CP_OEMCP,0,AddressOf(sData),-1,0,0)) to iLength
    If (iLength = 0) Function_Return ""

    // Create output buffer
    Move (Repeat(Character(0),iLength*2)) to sBuffer

    // Do actual conversion
    Move (MultiByteToWideChar(CP_OEMCP,0,AddressOf(sData),-1,AddressOf(sBuffer),iLength)) to iResult
    If (iResult = 0) Function_Return ""

    Function_Return sBuffer
End_Function
Then we need to convert this result into an array of UChar values. You do this with:
Code:
Function StrToUCharArray Global String sDataIn Returns UChar[]
    Integer iLength
    UChar[] ucResult
    Address aResult
    
    Move (Length (sDataIn)) to iLength
    Move (ResizeArray (ucResult, iLength)) to ucResult
    Move (AddressOf (ucResult)) to aResult
    Move sDataIn to aResult
    
    Function_Return ucResult
End_Function
Finally we can use this result with the ComWrite message. I like to split my code in smaller parts and thus I wrote:
Code:
// Display an HTML string in the browser
Procedure DisplayHTML String sHTML
    UChar[] ucHTML

    Move (StrToWStr (sHTML)) to sHTML
    Move (StrToUCharArray (sHTML)) to ucHTML
    Send ComWrite of oHTMLDocument ucHTML
End_Procedure
And the Close method? This depends on whether you want to write again or not. If you send Close you would need the Open again if you want to do more writing.

In a next blog I will continue with more extensions... keep tuned.
Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	TipOfTheDay.jpg 
Views:	2923 
Size:	75.3 KB 
ID:	4170   Click image for larger version. 

Name:	Import COM library.jpg 
Views:	3151 
Size:	110.4 KB 
ID:	4171  

Updated 2-Dec-2013 at 02:27 AM by Vincent Oorsprong (Images enlarged)

Categories
Uncategorized

Comments

  1. Bob Worsley's Avatar
    Exactly what I needed to display html email messages from my database!