2006. január 23., hétfő

From resources to TWebBrowser


Problem/Question/Abstract:

Ever wanted to fast do your own exe containing HTML pages. This way of doing, lets you easy manage HTML files included into your EXE in a TWebBrowser.

Answer:

First of all you need to include those two units:

uses mshtml, activex;

Next, you must insert a TWebBrowser (called "WB" in this article) into your form (called frmMain in this article).

You must add 2 public procedures, called "InternalPage" and "ResourcePage", to your form. After that, the declaration should look like this:

{...}
type
  TfrmMain = class(TForm)
    wb: TWebBrowser;
    {...}
  public
    procedure InternalPage(const HTMLString: string);
    procedure ResourcePage(const Name: string);
    {...}
  end;

The implementation of that procedures is this:

procedure tfrmmain.InternalPage(const HTMLString: string);
var
  pagesource: OleVariant;
  HTMLDocument: IHTMLDocument2;
begin
  if not (Assigned(WB.Document)) then
    WB.Navigate('about:blank', EmptyParam, EmptyParam, EmptyParam, EmptyParam);
  HTMLDocument := WB.Document as IHTMLDocument2;
  pagesource := VarArrayCreate([0, 0], varVariant);
  pagesource[0] := HTMLString;
  HTMLDocument.Write(PSafeArray(TVarData(pagesource).VArray));
  HTMLDocument.Close;
end;

procedure TfrmMain.ResourcePage(const Name: string);
var
  RS: TResourceStream;
  SL: TStringList;
begin
  try
    RS := TResourceStream.create(HInstance, uppercase(trim(Name)), RT_RCDATA);
    try
      SL := TStringList.create;
      try
        SL.LoadFromStream(RS);
        InternalPage(SL.Text);
      finally
        SL.Destroy;
      end;
    finally
      RS.Destroy;
    end;
  except
    on e: exception do
      ;
  end;
end;

The next move is to manage a little the BeforeNavigate2 event of our TWebBrowser. You only need to make this:

procedure TfrmMain.wbBeforeNavigate2(Sender: TObject;
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
  Headers: OleVariant; var Cancel: WordBool);
var
  pagename: string;
begin
  if lowercase(trim(url)) = 'about:blank' then
    exit;
  if pos('internal://', lowercase(URL)) = 1 then
  begin
    cancel := true;
    pagename := copy(URL, (pos('://', URL) + 3), maxint);
    if length(pagename) > 0 then
      if pagename[length(pagename)] = '/' then
        delete(pagename, length(pagename), 1);
    ResourcePage(pagename);
  end;
end;

Now, add, for example tro pages as RT_RCDATA into project's resources (Project\Resources menu into Delphi IDE, then right click on the toolwindow, and select New\User Data), called for example "FIRSTHTMLPAGE" and "SECONDHTMLPAGE".

On the Create event of your form you need to load the first page:

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  ResourcePage('FIRSTHTMLPAGE');
end;

That's all.

By the way: you'll need to refer all links in your page to "internal://" + name of the resource containing the page for it to work.

Here's a way to use this source code but without having to put "internal://" for every link inside to html files:

declare a TStringList:

private
{ Private declarations }
MyPages: TStringList;

...on the oncreate event add all your pages in the form:

MyPages := TStringList.Create;
MyPages.Add('about:blankdd.htm=HTMLFILE1');
MyPages.Add('about:blankddamigos.htm=HTMLFILE2');
MyPages.Add('about:blankddamigos2.htm=HTMLFILE3');
MyPages.Add('about:blankddamigos3.htm=HTMLFILE4')

  ...all your pages, note that I added 'about:blank' before the name of each html file... I don't know why, but it doesn't work without it, so... just put it...

then the BeforeNavigate2 event would look like:

procedure TForm1.wbBeforeNavigate2(Sender: TObject;
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
  Headers: OleVariant; var Cancel: WordBool);
var
  pagename: string;
begin
  if lowercase(trim(url)) = 'about:blank' then
    exit;
  pagename := MyPages.Values[url];
  if (PageName <> '') then
  begin
    Cancel := True;
    ResourcePage(pagename)
  end;
end;

That's it!

Of course both approaches have their advantages and disadvantages... I find this approach useful because you leave your html alone and you only worry about your Delphi Source code, like if there's more than one link to the same page (like from pages 2, 3, 4 to page 1) you don't need to make each of those links "internal://htmlfile1", this does it automatically for you.

Nincsenek megjegyzések:

Megjegyzés küldése