How to customise the TWebBrowser user interface (part 4 of 6)

Developing the customization class

As we decided above, TWBContainer will derive from TNulWBContainer and add all the required browser customization. Let us review what we want this customization to achieve:

  1. Display either the browser's built in pop-up menu or the menu assigned to the TWebBrowser.PopupMenu property.
  2. Display or hide 3D borders.
  3. Display or hide scroll bars.
  4. Customize document appearance at run time, via a custom cascading style sheet.
  5. Allow or inhibit users from selecting text in the browser control.
  6. The browser control will display XP themed controls (such as buttons) only when the host application is using themes.

We will define properties to configure the customization. We will then need to re-implement just two methods of IDocHostUIHandler to achieve the desired results – ShowContextMenu and GetHostInfo. ShowContextMenu controls the display of the popup menu and GetHostInfo enables the browser control's appearance to be customized.

Listing 12 shows the declaration of TWBContainer.

type
TWBContainer = class(TBaseContainer,
IDocHostUIHandler, IOleClientSite)
private
fUseCustomCtxMenu: Boolean;
fShowScrollBars: Boolean;
fShow3DBorder: Boolean;
fAllowTextSelection: Boolean;
fCSS: string;
protected
{ Re-implemented IDocHostUIHandler methods }
function ShowContextMenu(const dwID: DWORD;
const ppt: PPOINT; const pcmdtReserved: IUnknown;
const pdispReserved: IDispatch): HResult; stdcall;
function GetHostInfo(var pInfo: TDocHostUIInfo): HResult; stdcall;
public
constructor Create(const HostedBrowser: TWebBrowser);
property UseCustomCtxMenu: Boolean
read fUseCustomCtxMenu write fUseCustomCtxMenu default False;
property Show3DBorder: Boolean
read fShow3DBorder write fShow3DBorder default True;
property ShowScrollBars: Boolean
read fShowScrollBars write fShowScrollBars default True;
property AllowTextSelection: Boolean
read fAllowTextSelection write fAllowTextSelection default True;
property CSS: string
read fCSS write fCSS;
end;

Notice that we expose a property for each of the aspects of the browser control that we will customize, with the exception of XP theme support which we handle automatically. The properties are:

  • UseCustomCtxMenu – the browser control displays its default context menu when the property is false and uses the menu assigned to TWebBrowser PopupMenu when true. If UseContextMenu is true but PopupMenu is not assigned then no popup menu is displayed.
  • Show3DBorder – the web browser displays a 3D border when the property is true and no border when false.
  • ShowScrollBarsTWebBrowser displays scroll bars only if the property is true.
  • AllowTextSelection – permits text selection in the browser control when true and inhibits it when false.
  • CSS – provides the default cascading style sheet. This string property must contain valid CSS or be set to the empty string. When set we use this property to customize the document appearance.

The class constructor simply sets the default property values. These are chosen to leave the browser control in its default state. Listing 13 shows the constructor.

constructor TWBContainer.Create(const HostedBrowser: TWebBrowser);
begin
inherited;
fUseCustomCtxMenu := False;
fShowScrollBars := True;
fShow3DBorder := True;
fAllowTextSelection := True;
fCSS := '';
end;

Now we come on to the meat of the code. First let's look at how we re-implement ShowContextMenu. This is easier to write than to describe. See Listing 14 below.

function TWBContainer.ShowContextMenu(
const dwID: DWORD;
const ppt: PPOINT;
const pcmdtReserved: IInterface;
const pdispReserved: IDispatch): HResult;
begin
if fUseCustomCtxMenu then
begin
// tell IE we're handling the context menu
Result := S_OK;
if Assigned(HostedBrowser.PopupMenu) then
// browser has a pop up menu so activate it
HostedBrowser.PopupMenu.Popup(ppt.X, ppt.Y);
end
else
// tell IE to use default action: display own menu
Result := S_FALSE;
end;

We first check the fUseCustomCtxMenu field to see what to do. If it is false we simply return S_FALSE to tell the web browser we have not handled the context menu. This causes the browser to display its default pop-up menu.

When fUseCustomCtxMenu is true we return S_OK to show we are handling the context menu ourselves. This prevents the default pop-up menu from being displayed. If the browser control's PopupMenu property is set we display the menu by calling its Popup method. The ppt parameter supplies the co-ordinates where the mouse was right clicked. We use this to position the top left corner of the pop-up menu.

GetHostInfo is more complex because it determines the display of the border, scroll bars, text selection, XP theme support and the default style sheet. We instruct the browser control about how to handle these items by filling in a TDocHostUIInfo structure, a pointer to which is passed as a parameter to the method. Listing 15 defines this structure and comments describe its fields. Listing 16 presents GetHostInfo itself.

type
TDocHostUIInfo = record
cbSize: ULONG;          // size of structure in bytes
dwFlags: DWORD;         // flags that specify UI capabilities
dwDoubleClick: DWORD;   // specified response to double click
pchHostCss: PWChar;     // pointer to CSS rules
pchHostNS: PWChar;      // pointer to namespace list for custom tags
end;
function TWBContainer.GetHostInfo(
var pInfo: TDocHostUIInfo): HResult;
const
DOCHOSTUIFLAG_SCROLL_NO = $00000008;
DOCHOSTUIFLAG_NO3DBORDER = $00000004;
DOCHOSTUIFLAG_DIALOG = $00000001;
DOCHOSTUIFLAG_THEME = $00040000;
DOCHOSTUIFLAG_NOTHEME = $00080000;
begin
try
// Clear structure and set size
ZeroMemory(@pInfo, SizeOf(TDocHostUIInfo));
pInfo.cbSize := SizeOf(TDocHostUIInfo);
// Set scroll bar visibility
if not fShowScrollBars then
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_SCROLL_NO;
// Set border visibility
if not fShow3DBorder then
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_NO3DBORDER;
// Decide if text can be selected
if not fAllowTextSelection then
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_DIALOG;
// Ensure browser uses XP themes if application is doing
if ThemeServices.ThemesEnabled then
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_THEME
else if ThemeServices.ThemesAvailable then
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_NOTHEME;
// Record default CSS as Unicode
pInfo.pchHostCss := TaskAllocWideString(fCSS);
if not Assigned(pInfo.pchHostCss) then
raise Exception.Create('Task allocator can''t allocate CSS string');
// Return S_OK to indicate we've made changes
Result := S_OK;
except
// Return E_FAIL on error
Result := E_FAIL;
end;
end;
dwDoubleClick
This TDocHostUIInfo field is ignored by some browser versions.

For our purposes we only need to use the cbSize, dwFlags and pchHostCss fields of TDocHostUIInfo. We set the remaining fields to zero. cbSize is set to the size of the structure – a common Windows idiom.

Next we decide which flags to store in dwFlags to customize the control's user interface capabilities. Which flags we specify depends on the state of the ShowScrollBars, Show3DBorder and AllowTextSelection properties and whether XP themes are enabled. The flags we use are:

Other flags
There are numerous other DOCHOSTUIFLAG_ flags that can be assigned to dwFlags. The full set is defined in this article's demo code.
  • DOCHOSTUIFLAG_SCROLL_NO prevents the vertical scroll bar from being displayed.
  • DOCHOSTUIFLAG_NO3DBORDER inhibits the display of 3D borders.
  • Slightly less obviously, DOCHOSTUIFLAG_DIALOG prevents text selection – the name comes from the main use of this flag to prevent text selection in dialog boxes.
  • More complex is the way we ensure that the application's XP themes are echoed by the web browser control. We use the Themes unit's ThemeServices object to check if themes are enabled. If so we use the DOCHOSTUIFLAG_THEME flag to request that the browser also uses themes. If themes are not enabled, but available, we switch off the browser's theme support by using DOCHOSTUIFLAG_NOTHEME. If themes are not available at all (e.g. in Windows 2000) we do nothing.

Lastly, we store the default style sheet (per the CSS property) as Unicode text in the pchHostCss field. We have to allocate storage for this Unicode string – and we must use the task allocator to do this. Note that we are not responsible for freeing this memory – TWebBrowser does this.

We use the TaskAllocWideString helper function (from the CodeSnip Database) to allocate the storage and convert the string to Unicode. The routine is shown in Listing 17.

function TaskAllocWideString(const S: string): PWChar;
var
StrLen: Integer;  // length of string in bytes
begin
// Store length of string in characters, allowing for terminal #0
StrLen := Length(S) + 1;
// Allocate buffer for wide string using task allocator
Result := CoTaskMemAlloc(StrLen * SizeOf(WideChar));
if Assigned(Result) then
// Convert string to wide string and store in buffer
StringToWideChar(S, Result, StrLen);
end;
Other examples
Article #22: "How to call Delphi code from scripts running in a TWebBrowser" demonstrates another class that descends from TNulWBContainer, this time re-implementing the GetExternal method.

And that completes the browser customization code. If you need to specify different customizations simply define a new class that descends from TNulWBContainer and provide the required functionality by re-implementing the appropriate IDocHostUIHandler methods.

To use our customization class simply create an instance of TWBContainer, passing a reference to the browser control that is to be customized, set the required properties of TWBContainer then load the required document.

Note: You should always set the properties of TWBContainer before loading any document into the browser control. The reason for this is that on operating systems earlier than Windows XP SP2 the control only reads the default CSS when the first document is loaded. Consequently, changing the CSS property after loading the first document will have no effect.

In the next section we will test our code by creating a sample application that exercises TWBContainer.

posted @ 2008-10-15 10:22  Max Woods  阅读(446)  评论(0编辑  收藏  举报