btbtnHelpClick on Child form not raised from Inherited form

This section is for programmers. Please use it for all discussons on interfacing help with your applications and related subjects.

Moderators: Alexander Halser, Michael Schwarzl

Post Reply
Rick Anderson
Posts: 4
Joined: Sat Nov 21, 2009 2:35 am

btbtnHelpClick on Child form not raised from Inherited form

Unread post by Rick Anderson »

Environment: Delphi 7 and using the EHS THelpRouter component and just switched to CHM help format.
Our existing help (.HLP) is very outdated and we do not have the source, context references, etc and so we are re-writing the help in CHM format and are under a tight deadline and so we are not putting in any form/field context numbers or trying to match any existing context numbers. All we want to do is show a help file with the TOC showing on the left and the main page on the right. Nothing fancy.

Problem:
We have a "child" form that is the base for most of the forms in our application. This child form has a set of buttons (TbitBtn), one of which triggers the showing of our help file (see below)

Code: Select all

procedure TfrmMXTChild.btbtnHelpClick(Sender: TObject);
begin
    frmMXTMain.hrHelp.Helpfile := Application.HelpFile;
    frmMXTMain.hrHelp.HelpType := htHTMLhelp;
    frmMXTMain.hrHelp.HelpContent;
end;
We also have set the BorderIcons.biHelp = True on the frmChild.

This works great on most of the forms but there are several that ignored the btbtnHelpClick event (put a breakpoint on the first line of the event and it was never triggered). We verified that the troublesome form was inheriting from frmChild, that the button events were associated, etc. We finally discovered the one difference between the forms that work and the forms that don't work. The form HelpContext on frmChild is = 0 and on all the inherited forms that don't work, the HelpContext was changed to a non-zero value (all positive numbers so far) and on the inherited forms that do show the help file the HelpContext is = 0. I was able to work around the issue by adding the following code to the frmChild.FormCreate event

Code: Select all

  if self.HelpContext <> 0 then
    self.HelpContext := 0;
Question:
My question is why is the btbtnHelp's OnClick event being bypassed when the HelpContext on the form is something other than zero?

Thanks
Rick Anderson
User avatar
Alexander Halser
EC-Software Support
Posts: 4098
Joined: Mon Jun 24, 2002 7:24 pm
Location: Salzburg, Austria
Contact:

Re: btbtnHelpClick on Child form not raised from Inherited f

Unread post by Alexander Halser »

The button OnClick event just executes what's in there. So it calls the table of contents - your code simply ignores the HelpContext property of the form. There is no hidden automatism in the click event of a button.

In Help & Manual, we use THelpRouter, too. As a rule of thumb, every dialog has a help context number with a corresponding HTML topic. We connect the Help button of each dialog to a central execution procedure:

Code: Select all

procedure TDialogForm.btnHelpClick(Sender: TObject);
begin
  HMFormLevelHelp(self);
end;
The function called is in a separate unit, which is referenced by all forms of the application.

Code: Select all

procedure HMFormLevelHelp(Sender: TObject);
begin
  if TForm(Sender).helpcontext <> 0 then  //if help context assigned, call context topic
  begin  
    Application.HelpCommand(HELP_CONTENTS, 0);
    Application.HelpCommand(HELP_CONTEXT, TForm(Sender).helpcontext);
  end
  else
    Application.HelpCommand(HELP_FINDER, 0);  //else display TOC
end;
The actual help commands are routed by a single THelpRouter instance on the main form and redirected to HTML Help.

To work around the limitation that CHMs can't be displayed when located on a network drive, THelpRouter first checks if the CHM file is local and if it isn't, it first makes a temporary copy. This is an extra step that has nothing to do with context help, but it served us well for the last five years:

Code: Select all

function TFrmMain.HelpRouterHelp(Command: Word; Data: Integer; var CallHelp: Boolean): Boolean;

  function GetTempDir: String;
  var
    TmpPath: PChar;
  begin
    try
      GetMem(TmpPath, MAX_PATH);
      FillChar(TmpPath^, MAX_PATH, #0);
      GetTempPath(MAX_PATH, TmpPath);
      Result := ExpandFileName(TmpPath);
    finally
      FreeMem(TmpPath, MAX_PATH);
    end;
  end;

  function GetFileSize(const FileName: String): Integer; //Int64;
  var
    FileStream: TFileStream;
  begin
    FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
    try
      try
        Result := FileStream.Size;
      except
        Result := 0;
      end;
    finally
      FileStream.Free;
    end;
  end;

var
  strOriginalHelp, strLocalHelp: String;
  bLocal: boolean;
begin
     { 28.1.2007
       Workaround for the Microsoft Security Update 896358 (http://www.microsoft.com/technet/security/bulletin/ms05-026.mspx):
       When a CHM file is opened from a network drive, topics will not be displayed. This is actually not
       a CHM thing, but an MSIE restriction. We cannot safely detect whether MSIE will be able
       to display the topic page, we can only assume that it will not be able to. 

       Only at the very FIRST HELP ATTEMPT and only if we are running from
       a network drive. We also test for UNC path names, since GetDriveType doesn't
       always reliably return the drive type }

  if (not HHLoaded) and ((copy(application.exename, 1, 2) = '\\') or (GetDriveType(PChar(extractFilePath(application.exename))) = DRIVE_REMOTE)) then
  begin
          { Here we go, we need a local copy of HELPMAN.CHM ... First check if a
            copy of the help file is already there (we do not delete this file)
            and if it is, check if it's up-to-date. We go by the file SIZE here.
            This is not 100% perfect but sufficient for our own help file. }

    strOriginalHelp := Application.Helpfile;
    if FileExists(strOriginalHelp) then
    begin
      strLocalHelp := GetTempDir + '\' + extractFileName(Application.Helpfile);

      bLocal := FileExists(strLocalHelp) and (GetFileSize(strOriginalHelp) = GetFileSize(strLocalHelp));
      if not bLocal then
        bLocal := CopyFile(PChar(strOriginalHelp), PChar(strLocalHelp), False);
      if bLocal then
        Application.Helpfile := strLocalHelp;  //assign local help file
    end;
  end;
end;
Alexander Halser
Senior Software Architect, EC Software GmbH
Rick Anderson
Posts: 4
Joined: Sat Nov 21, 2009 2:35 am

Re: btbtnHelpClick on Child form not raised from Inherited f

Unread post by Rick Anderson »

Thank you for the quick reply. Your code example was very interesting and useful. What we are still trying to figure out is why the button click event is being ignored when the HelpContext property is <>0. Please see the pseudo-code below:

Code: Select all

frmMain (Main Form)
   ...
   hrHelp : THelpRouter
   OnLoad event
      Set Application.HelpFile to CHM
      Set hrHelp.HelpFile to Application.HelpFile
   ...

frmChild (used as base for most forms)
   ...
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]
   FormStyle = fsMDIChild
   object btbtnHelp: TBitBtn
      Hint = '|Get help...'
      OnClick = btbtnHelpClick
      Kind = bkHelp
   end
      btbtnHelpClick Event
          frmMXTMain.hrHelp.Helpfile := Application.HelpFile; (because on other main form menu items we point to other CHM files and need to reset to main application help file)
          frmMXTMain.hrHelp.HelpType := htHTMLhelp;
          frmMXTMain.hrHelp.HelpContent;        
      end

frmGoodScreen
   inherits from frmChild
   all inherited properties (BorderStyle, button events, etc) point to the frmChild
   frmGoodScreen.HelpContext = 0

frmBadScreen         
   inherits from frmChild
   all inherited properties (BorderStyle, button events, etc) point to the frmChild
   frmBadScreen.HelpContext = some positve number > 0

Set Breakpoint in frmChild.btbtnHelpClick event at first line

Run the application
   Open frmGoodScreen
   Click on the btbtnHelp
      Code stops execution at the breakpoint
      Step through the code to end of procedure, everything is fine
      CHM help is displayed

   Open frmBadScreen
   Click on the btbtnHelp
      Nothing, nada, zilch code does not stop at breakpoint and nothing happens. No errors
      We verified on second attempt that all the properties and events were correct.

Closed application
Changed the frmBadScreen.HelpContext to 0
Run the application
   Open frmBadScreen
   Click on the btbtnHelp
      Code stops execution at the breakpoint
      Step through the code to end of procedure, everything is fine
      CHM help is displayed
It is completely repeatable depending on the value in the forms HelpContext. I have been able to fix the immediate problem but I am trying to understand why having a form's HelpContext = some value >0 would prevent the execution of it's child form events. Is this somehow related to the F1 redirecting issue with Delphi 6/7? The frmChild.FormCreate runs when the frmGoodScreen or frmBadScreen is created so I know that frmChild events are running when an inherited form is running. Other buttons (of the same TBitBtn type) work. It is only this button that has this issue.

Should I be using the HelpCommand(HELP_CONTENTS, 0) and HelpCommand(HELP_CONTEXT, helpcontext) like in your example?


Thanks in advance for any thoughts
Rick
User avatar
Alexander Halser
EC-Software Support
Posts: 4098
Joined: Mon Jun 24, 2002 7:24 pm
Location: Salzburg, Austria
Contact:

Re: btbtnHelpClick on Child form not raised from Inherited f

Unread post by Alexander Halser »

I am pretty much sure that the onclick event is never bypassed when you click the button, otherwise I'd worry about the compiler. I cannot say anything about your form inheritance, however. Does my example in the previous post work?
Alexander Halser
Senior Software Architect, EC Software GmbH
Post Reply