View RSS Feed

Development Team Blog

Getting More Out of Your Browser Control - Part III

Rate this Entry
This is the third blog article about getting more out of the web browser control. In comments I have been asked to show code that uses the web browser control. In the first blog I mentioned that the web browser control is used in a welcome dialog for Database Explorer. Let's review the code for that dialog in this blog.

The dialog
Let's look at the dialog again, the picture shows how it looks like.

The dialog is a Visual DataFlex ModalDialog object containing a cComWebBrowser, a checkbox and 2 button objects. The buttons can be used to browse to the next tip and to close the dialog. The checkbox is meant to please the user with an option to turn off the welcome dialog display.

Show Tips At Startup
The checkbox should write the user preference in a persistent way. For me there are three locations; 1) a table in your database with user preferences, 2) an INI file on disk or 3) in the Windows registry. In the following code I used the Windows registry. If the user toggles the state of the checkbox a true or false value will be directly written to the registry. I use the WriteDWord method of the cApplication object which places the preference in the HKEY_CURRENT_USER branch of the registry.
Code:
Object oShowTipsCheckBox is a CheckBox
    Set Size to 10 50
    Set Location to 164 5
    Set Label to "Show Tips at Startup"
    Set peAnchors to anBottomLeft
    Set Checked_State to True
    
    Procedure OnChange
        Boolean bChecked
        
        Get Checked_State to bChecked
        Send WriteDword of ghoApplication "Welcome" "ShowTips" bChecked
    End_Procedure
End_Object
Using the registry is easy but if the user often uses a different computer and you do not store and restore the user settings via a network policy you are better off with one of the two other mentioned locations for storing the preference.

The Tips
As you can see in Database Explorer and the screenshot in the beginning of this blog it is possible for the user to browse through a set of tips. Tips are "little" text strings and need to be stored or read from somewhere. For Database Explorer I choose to store the tips inside the executable so that we do not depend on a file on disk but for the blog I created a table with tips. The advantage of a database table with tips is that you can easily modify the tips (extend, refresh). Another idea is to store the tips in a webservice. This way you can also keep track of how many different workstations are accessing your application because you can access the IP address. I would remove the option to turn off the show tips at startup if you want to use the feature as a kind of user access control.

The tips are stored in a table named Tips with two columns; an ID column for the tip number and a TEXT column for the text to be displayed. I made the TEXT column 512 characters in size.
Code:
-----------------------------------------------------------------------------
  DATE: 17-04-2011      TIME: 09:59                                  PAGE:  1
  FILE DEFINITION FOR FILE: Tips (# 1)
-----------------------------------------------------------------------------
  DRIVER NAME               : DATAFLEX
  FILE ROOT NAME            : Tips
  USER DISPLAY NAME         : Tips
  DATAFLEX FILE NAME        : Tips
-----------------------------------------------------------------------------
  RECORD LENGTH             : 640       ( USED: 514 )
  MAX NUMBER OF RECORDS     : 10000     ( USED: 3 )
  FILE COMPRESSION          : NONE
  RE-USE DELETED SPACE      : YES
  LOCKING TYPE              : FILE
  HEADER INTEGRITY CHECKING : YES
  TRANSACTION TYPE          : CLIENT ATOMIC
  RECORD IDENTITY INDEX     : 0 ( 0 , 0 )
  FILE LOGIN PARAMETER      : 
  SYSTEM FILE               : NO 
-----------------------------------------------------------------------------

NUM  FIELD NAME       TYPE SIZE  OFFST IX   RELATES TO FILE.FIELD
---  ---------------  ---- ----- ----- --   ---------------------------------
  1  ID               NUM    4.0     1  1   
  2  Text             TEX    512     3      


INDEX# FIELDS          DES U/C    LENGTH LEVELS SEGMENTS MODE
------ --------------- --- ---    ------ ------ -------- -------
  1    ID              NO  NO       2      2       1     ON-LINE
The tip to be displayed is retrieved via a self defined method which I named FindNextTip. The method is called from when the first tip needs to be displayed in the cComWebBrowser object as well as from the button which lets the user browse to the next tip.
The source code I wrote for this is pretty straight forward for DataFlex developers and is:
Code:
Procedure FindNextTip
    Integer iCurrentTip iTips iTip

    Get piCurrentTip to iCurrentTip
    Get_Attribute DF_FILE_RECORDS_USED of Tips.File_Number to iTips
    
    Repeat
        Move (Random (iTips) + 1) to iTip
        Clear Tips
        Move iTip to Tips.ID
        Find Eq Tips by 1
    Until (Found and iTip <> iCurrentTip)
    
    Set piCurrentTip to iTip                        
End_Procedure
With the piCurrentTip property I keep track of the current tip number and in the loop we randomly choose a tip number from the amount of records in the table. Instead of randomly pick a tip you could just take the next record and if you reach the end of the file go back to the beginning.

Display the Tip
With the information from the previous blogs you can write the code for the cComWebbrowser object to find the location to replace with the text and ID of the current tips record. The code for the cComWebBrowser object used for this blog is:
Code:
Object oDisplay is a cComWebBrowser
    Set Size to 155 326
    Set Location to 5 5
    Set peAnchors to anAll
    
    { DesignTime = False }
    Property Integer piCurrentTip
    
    Object oHTMLDocument is a cComHTMLDocument
    End_Object
    
    Object oHTMLDivElement is a cComHTMLDivElement
    End_Object
    
    Procedure OnCreate
        String sImgSrc sHTML
        UChar[] ucHTML
        Variant vDocument
        
        Forward Send OnCreate
        
        Send ComNavigate "about:blank" 0 0 0 0
    
        Get_File_Path "welcome.png" to sImgSrc
        Move ('<' + 'html>') to sHTML
        Move (sHTML + '<' + body style="font-family: Lucida Sans Unicode, Lucida Grande, Verdana, Arial, Helvetica, sans-serif; background-color:#C0DCFE; height: 100%; width: 100%; font-size: 11px;padding: 5px 10px 5px 10px;">') to sHTML
        Move (sHTML + '<' + 'div id="left" style="float: left;">') to sHTML
        Move (sHTML + '<' + 'img height="150" width="150" alt="welcome.jpg" src="' - sImgSrc - '"/>') to sHTML
        Move (sHTML + '<' + '/div>') to sHTML
        Move (sHTML + '<' + 'div id="right" style="float: left;">') to sHTML
        Move (sHTML + '<' + 'h1>Did You Know?<' + '/h1>') to sHTML
        Move (sHTML + '<' + div id="TipId" style="margin-bottom: 4px; font-style: italic;"> <' + '/div>') to sHTML
        Move (sHTML + '<' + div id="WelcomeTip"> <' + '/div>') to sHTML
        Move (sHTML + '<' + /div><' + '/body><' + '/html>') to sHTML
        Move (StrToWStr (sHTML)) to sHTML
        Move (StrToUCharArray (sHTML)) to ucHTML
        
        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
        Send ComWrite of oHTMLDocument ucHTML
        Send ComClose of oHTMLDocument
        
        Send ShowNextTip
    End_Procedure
End_Object
The HTML instructions in the DataFlex strings above had to be cut in multiple blocks of text to avoid that the browser that you use to read this article tries to parse it as valid HTML.

In the OnCreate method above we create a default HTML page, light blue colored with a nice image on the left, a header for the text etc. Make sure that if you copy the code the div elements are lined up as in the example as a slightly longer text or bigger image can completely destroy the layout of the tip text page.

At the end of the OnCreate event we call a method named ShowNextTip which on its turn calls FindNextTip to locate a next tip record. In the ShowNextTip we locate the two div elements in the HTML page and replace the contents. The code for that is:
Code:
Procedure ShowNextTip
    Variant vDocument vElement
    
    Send FindNextTip
   
    Get ComDocument to vDocument
    Set pvComObject of oHTMLDocument to vDocument
    Get ComGetElementById of oHTMLDocument "WelcomeTip" to vElement
    If (not (IsNullComObject (vElement))) Begin
        Set pvComObject of oHTMLDivElement to vElement
        Set ComInnerHTML of oHTMLDivElement to (Trim (Tips.Text))
    End
    Get ComGetElementById of oHTMLDocument "TipId" to vElement
    If (not (IsNullComObject (vElement))) Begin
        Set pvComObject of oHTMLDivElement to vElement
        Set ComInnerHTML of oHTMLDivElement to (Sformat ("Tip: %1", Tips.ID))
    End
End_Procedure
The full source code for the welcome / tip dialog is as follows:
Code:
Use Windows.pkg
Use WelcomeHMTL.pkg
Use CharTranslate.pkg

Open Tips
Set_Attribute DF_FILE_MODE of Tips.File_Number to DF_FILEMODE_READONLY

// 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

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

Object oWelcomeDialog is a ModalPanel
    Set Size to 180 337
    Set Label to "Welcome"
    Set piMinSize to 89 211
    Set Location to 2 2
    
    Procedure Page_Object Boolean bPage
        Forward Send Page_Object bPage
        
        If (bPage) Begin
            Set Icon to "Default.Ico"
        End
    End_Procedure

    Object oNextTipButton is a Button
        Set Label to "&Next Tip"
        Set Location to 164 227
        Set peAnchors to anBottomRight
        Set Default_State to True

        Procedure OnClick
            Send ShowNextTip of oDisplay
        End_Procedure
    End_Object

    Object oCloseButton is a Button
        Set Label to "&Close"
        Set Location to 164 282
        Set peAnchors to anBottomRight
        Set Default_State to True

        Procedure OnClick
            Send Close_Panel
        End_Procedure
    End_Object

    Object oDisplay is a cComWebBrowser
        Set Size to 155 326
        Set Location to 5 5
        Set peAnchors to anAll
        
        { DesignTime = False }
        Property Integer piCurrentTip
        
        Object oHTMLDocument is a cComHTMLDocument
        End_Object
    
        Object oHTMLDivElement is a cComHTMLDivElement
        End_Object

        Procedure OnCreate
            String sImgSrc sHTML
            UChar[] ucHTML
            Variant vDocument
            
            Forward Send OnCreate
            
            Send ComNavigate "about:blank" 0 0 0 0

            Get_File_Path "welcome.png" to sImgSrc
            Move ('<' + 'html>') to sHTML
            Move (sHTML + '<' + body style="font-family: Lucida Sans Unicode, Lucida Grande, Verdana, Arial, Helvetica, sans-serif; background-color:#C0DCFE; height: 100%; width: 100%; font-size: 11px;padding: 5px 10px 5px 10px;">') to sHTML
            Move (sHTML + '<' + 'div id="left" style="float: left;">') to sHTML
            Move (sHTML + '<' + 'img height="150" width="150" alt="welcome.jpg" src="' - sImgSrc - '"/>') to sHTML
            Move (sHTML + '<' + '/div>') to sHTML
            Move (sHTML + '<' + 'div id="right" style="float: left;">') to sHTML
            Move (sHTML + '<' + 'h1>Did You Know?<' + '/h1>') to sHTML
            Move (sHTML + '<' + div id="TipId" style="margin-bottom: 4px; font-style: italic;"> <' + '/div>') to sHTML
            Move (sHTML + '<' + div id="WelcomeTip"> <' + '/div>') to sHTML
            Move (sHTML + '<' + /div><' + '/body><' + '/html>') to sHTML
            Move (StrToWStr (sHTML)) to sHTML
            Move (StrToUCharArray (sHTML)) to ucHTML
            
            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
            Send ComWrite of oHTMLDocument ucHTML
            Send ComClose of oHTMLDocument
            
            Send ShowNextTip
        End_Procedure
        
        Procedure FindNextTip
            Integer iCurrentTip iTips iTip

            Get piCurrentTip to iCurrentTip
            Get_Attribute DF_FILE_RECORDS_USED of Tips.File_Number to iTips
            
            Repeat
                Move (Random (iTips) + 1) to iTip
                Clear Tips
                Move iTip to Tips.ID
                Find Eq Tips by 1
            Until (Found and iTip <> iCurrentTip)
            
            Set piCurrentTip to iTip                        
        End_Procedure
        
        Procedure ShowNextTip
            Variant vDocument vElement
            
            Send FindNextTip
           
            Get ComDocument to vDocument
            Set pvComObject of oHTMLDocument to vDocument
            Get ComGetElementById of oHTMLDocument "WelcomeTip" to vElement
            If (not (IsNullComObject (vElement))) Begin
                Set pvComObject of oHTMLDivElement to vElement
                Set ComInnerHTML of oHTMLDivElement to (Trim (Tips.Text))
            End
            Get ComGetElementById of oHTMLDocument "TipId" to vElement
            If (not (IsNullComObject (vElement))) Begin
                Set pvComObject of oHTMLDivElement to vElement
                Set ComInnerHTML of oHTMLDivElement to (Sformat ("Tip: %1", Tips.ID))
            End
        End_Procedure
    End_Object

    Object oShowTipsCheckBox is a CheckBox
        Set Size to 10 50
        Set Location to 164 5
        Set Label to "Show Tips at Startup"
        Set peAnchors to anBottomLeft
        Set Checked_State to True
        
        Procedure OnChange
            Boolean bChecked
            
            Get Checked_State to bChecked
            Send WriteDword of ghoApplication "Welcome" "ShowTips" bChecked
        End_Procedure
    End_Object

    On_Key Key_Alt+Key_C Send KeyAction of oCloseButton
    On_Key Key_Alt+Key_N Send KeyAction of oNextTipButton
End_Object
Feel free to use the code above in your own applications.
Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	2011-04-16_171510.jpg 
Views:	2638 
Size:	62.0 KB 
ID:	4257  

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

Categories
Uncategorized

Comments