Autor Beitrag
Logikmensch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Do 13.04.06 06:11 
Hallo liebe Delphi-Forum-Mitglieder und -Leser.

An mich wurde die Bitte gerichtet, mal ein Tut für die Einbindung von Hilfedateien in Delphi zu schreiben. Das ist auch mein erstes Tutorial und ich hoffe, es wirkt nicht arrogant oder schlauschwätzerisch. Das bin ich nämlich nicht bzw. möchte ich nicht sein. Im Gegenteil - ich weiß selber nicht alles darüber und vor kurzem habe ich genauso auf dem Schlauch gestanden wie viele andere.
Nach dem Durchlesen des Tutorials könnt ihr folgendes und euer Programm verfügt über folgende Fähigkeiten:

- Mit dem kostenlosen und frei erhältlichen Programm HelpMaker eine Hilfedatei schreiben und in das HLP und CHM-Format umwandeln (es gehen natürlich auch andere Tools wie Help&Manual oder HelpBreeze).
- Nach dem Anklicken eines Togglebuttons auf eurem Formular (z.B. ein TSpeedbutton oder TToolButton) ändert sich der Mauszeiger in das bekannte Kontexthilfe-Symbol. Damit geht ihr auf ein beliebiges Control eures Formulars und bekommt dann die entsprechende Kontexthilfe eurer Hilfedatei angezeigt.
- Alternativ kann auch ein Menüpunkt des TMainMenu mit dieser Funktion ausgestattet werden (ist hier im Beispiel mit integriert).

Ich bin eigentlich kein Mensch der (über-) langen Reden, und deshalb will ich gleich zur Sache kommen. :-)

Grundlegende Informationen zu dem Source:
Ihr braucht auf eurem Formular zur Einbindung meines Codes nur einen TSpeedButton, den ihr umbenennt in contexthelpbutton, ein TMainMenu, in dem ihr einen Menüpunkt "Hilfe" anlegt und diesen umbenennt in Menu_Help und eine ApplicationEvents - Komponente. Anschließend müsst ihr noch die angegebenen Ereignisprozeduren einbinden. Wer es einfacher haben will oder nur mal schauen möchte, ob ihm meine Lösung überhaupt zusagt, der kann auch einfach den Source meiner Testanwendung runterladen. Eine kleine Hilfedatei ist auch mit dabei.

Erstellen der Hilfedatei:
Ihr könnt wirklich das kostenlose Programm HelpMaker nehmen, es bietet eigentlich alles, was man braucht, um HLP-, CHM- und auch andere Formate zu erzeugen. HLP-Dateien nimmt man, wenn man in Puncto Kompatibilität auf der sicheren Seite sein möchte. Allerdings ist HTML-Help (zu dem auch das kompilierte CHM-Format gehört) leistungsstärker und, wie ich finde, auch schöner. HelpMaker findet man auf der Webseite www.vizacc.com/ .
Man bekommt (wie auch in anderen Help-Editoren) eine Baumansicht auf der linken Seite und eine Detailansicht jedes Baumzweiges in der rechten Seite. Beides stellt letztendlich die Struktur und den Inhalt des Hilfetextes dar. Ich denke, mehr muss man dazu eigentlich nicht sagen, da die Programme alle sehr intuitiv benutzbar sind.
Jeder später von der eigentlichen Anwendung anzuspringende Hilfepunkt sollte eine sogenannte Help-ID bekommen, dies ist ein Integerwert <>0. Meist arbeitet man in 10er-Schritten. Man sollte diese IDs auch bei späteren Änderungen des Hilfstextes beibehalten.

Nach dem Erstellen des Hilfetextes muss man diesen noch "compilieren". Die meisten Programme (auch HelpMaker) bieten dafür mehrere Ausgabeformate an, z.B. HLP oder CHM.
Aufpassen muss man auch bei der Grafikeinbindung, denn z.B. ist es (meines Wissens nach) nicht möglich, JPG-Grafiken in HLP-Dateien einzubinden. Ich musste jdf. meine bisherigen HLP-Dateien immer mit BMP-Grafiken erstellen, die ich auf 16-bit-Farbtiefe runtergeschrumpft hatte. Mit HTML-Help (CHM-Dateien) gibt es soweit ich weiß keine solche Beschränkungen. Auch Tabellen werden hier vernünftig unterstützt. Es mag aber ältere Betriebssysteme geben (z.B. Windows 95), die das Format nicht direkt unterstützen.

Einbinden der Hilfe in Delphi:
Ich muss wirklich davon abraten, die Hilfedatei über den Menüpunkt Projekt - Optionen im Delphi direkt einzugeben. Das hat nämlich den schwerwiegenden Nachteil, dass dann der Pfad von eurer Festplatte mit in der Anwendung angegeben wird.
Mein Beispiel setzt diese Angabe (Application.HelpFile) erst zur Laufzeit und ermittelt vorher den Pfad der Anwendung selbst. Die Hilfedatei (hier MeineHilfe.CHM) muss dann nur im Verzeichnis eurer Anwendung stehen. Dann läuft die Hilfe auch, wenn sie nicht auf eurer Festplatte gestartet wird. ;-)


Nachfolgend gehe ich Stück für Stück auf den Code meines Beispiels ein. Ich denke, die meisten Fragen dürften sich dann automatisch klären. Wer noch Fragen hat - bitte sehr, vielleicht kann ich sie beantworten. ;-)

Hierzu gibt's noch nicht viel zu sagen. Dieser Code wird eh automatisch erzeugt:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, AppEvnts, Buttons, Menus, StdCtrls, ExtCtrls, ToolWin, ComCtrls;

type
  Tmainform = class(TForm)
    ApplicationEvents1: TApplicationEvents;
    MainMenu1: TMainMenu;
    Menu_ContextHelp: TMenuItem;
    ToolBar1: TToolBar;
    contexthelpbutton: TSpeedButton;
    Panel1: TPanel;
    Edit1: TEdit;
    Button1: TButton;
    Label1: TLabel;
    procedure Menu_ContextHelpClick(Sender: TObject);
    procedure contexthelpbuttonClick(Sender: TObject);
    function FormHelp(Command: Word; Data: Integer;
      var CallHelp: Boolean): Boolean;
    procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }


innerhalb der Privaten Deklarationen benötigen wir allerdings zwei neue Properties MOUSECURSOR und HELPMODE, die miteinander interagieren. Mit HELPMODE kann man dann direkt die Kontextsensitive Maus ein- und ausschalten.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
    Fmousecursor:TCursor;
    Fhelpmode:boolean;
    procedure sethelpmode(value:boolean);
    procedure setmousecursor(value:TCursor);
  public
    { Public-Deklarationen }
    property helpmode:boolean read Fhelpmode write sethelpmode;
    property mousecursor:TCursor read Fmousecursor write setmousecursor;
  end;

var
  mainform: Tmainform;

implementation

{$R *.dfm}


Hier geht's jetzt schon zu den (im grunde auch global definierbaren) Prozeduren.
COMMONFORMHELP ist in der Lage, sowohl HLP- als auch CHM-Dateien aufzurufen. Somit könnt ihr, ohne euren Code umzuändern, direkt den Dateinamen eurer Hilfedatei anpassen, wie ihr wollt. Wichtig ist hier, dass bei der HTML-Hilfe mit compilierten CHM-Dateien nicht der Wert von COMMAND, sondern die Konstante HH_HELP_CONTEXT übergeben wird, ansonsten wird der Kontext beim Öffnen der Hilfedatei nicht angesteuert.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function CommonFormHelp(Command: Word; Data: Integer;
  var CallHelp: Boolean): Boolean;
  //Kann HLP- oder CHM-Hilfe aufrufen. Die Hilfedatei muss vorher in Application.Helpfile gesetzt worden sein
var
  ext:String;
begin
  ext:=uppercase(extractfileext(application.HelpFile));
  if ext='.HLP' then result:=winhelp(application.Handle,PChar(application.HelpFile),command,data)
    else
  if ext='.CHM' then result:=htmlhelp(application.Handle,PChar(application.HelpFile),HH_HELP_CONTEXT,data)<>0
    else result:=false;
  CallHelp:=result;
end;


SETALLMOUSECURSORS geht alle Kompos des Objektes CTL durch und setzt deren HelpContexts auf den Wert C. Dabei solltet ihr aber noch mal prüfen, ob eure Kompos auch den Mauscursor auf CrDefault stehen haben. Wird HELPMODE auf True gesetzt, werden dadurch alle Kompo-Mauszeiger Eurer Form auf CrHelp gesetzt, und zwar solange, bis die Hilfe abgearbeitet wurde.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
procedure SetAllMouseCursors(ctl:TObject; c:TCursor);
  //Setzt Mauscursor aller Teile eines Elementes, incl. aller Unterelemente auf C
var
  i:integer;
begin
  if ctl<>nil then begin
    if ctl is TWinControl then begin
      with TWinControl(ctl) do
        if visible then begin
          cursor:=c;
          for i:=0 to controlcount-1 do
            SetAllMouseCursors(controls[i],c);
        end{if}
    end
      else
    if ctl is TControl then begin
      with TControl(ctl) do
        if visible then cursor:=c;
    end{if}
  end{if}
end;


Die Funktion FINDHELPCONTEXT durchsucht ausgehend von der Mausposition PT die entsprechende Komponente und gibt deren HelpContext zurück.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
function FindHelpContext(ctl:TObject; pt:TPoint):integer;
  //Findet die zu einer Mausposition PT gehörende Komponente bzw. deren Hilfekontext-Nr.
var
  tc:integer;
  tctl:TControl;
begin
  result:=0;
  if ctl<>nil then begin
    if ctl is TWinControl then begin
      with TWinControl(ctl) do begin
        if helpcontext<>0 then result:=helpcontext;
        tctl:=ControlAtPos(screentoclient(pt),true,true);
        tc:=FindHelpContext(tctl,pt);
        if tc<>0 then result:=tc;
      end{with}
    end
      else
    if ctl is TControl then begin
      with TControl(ctl) do
        if helpcontext<>0 then result:=helpcontext;
    end{if}
  end{if}
end;


Nachfolgend die Initialisierungen im FORMCREATE-Ereignis:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure Tmainform.FormCreate(Sender: TObject);
var
  applicationpath:string;
begin
  helpmode:=false;
  //Ermitteln des Programmverzeichnisses. Dort soll auch die Hilfedatei stehen:
  applicationpath:=extractfiledir(paramstr(0));
  if (length(applicationpath)>0and (applicationpath[length(applicationpath)]<>'\'then
    applicationpath:=applicationpath+'\';
  //Setzen der Hilfetextdatei (nicht unter Projektoptionen machen wg. Pfad!!!)
  application.HelpFile:=applicationpath+'meinehilfe.chm';
end;


Nachfolgende Ereignisprozedur fängt Mausklicks ab und löscht diese, falls HELPMODE=True ist. Stattdessen wird die der angeklickten Komponente entsprechende Help-ID gesucht und die Hilfe ausgelöst.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure Tmainform.ApplicationEvents1Message(var Msg: tagMSG;  var Handled: Boolean);
  //Ereignisprozedur von TApplicationsEvent-Objekt (muss auf Form platziert werden)
var
  hc:integer;
begin
  if helpmode and (msg.message=WM_LBUTTONDOWN) or (msg.message=WM_RBUTTONDOWN) then begin
    //Wenn Helpmode aktiv, dann nicht die Mausoperation ausführen, sondern Hilfe anzeigen
    msg.message:=0//Message abfangen
    helpmode:=false;
    hc:=FindHelpContext(self,msg.pt);
    if hc<>0 then application.HelpContext(hc);
    handled:=true;
  end{if}
end;


Nachfolgend die Implementierungen der Properties HELPMODE und MOUSECURSOR.

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
function Tmainform.FormHelp(Command: Word; Data: Integer;
  var CallHelp: Boolean): Boolean;
  //Ereignisprozedur des Hauptformulars (wird bei F1-Taste und Hilemaus ausgelöst)
begin
  result:=CommonFormHelp(command,data,callhelp);
end;

procedure Tmainform.sethelpmode;
begin
  Fhelpmode:=value;
  contexthelpbutton.Down:=value; //Wenn Hilfe erledigt, gedrückten ContextHelpButton toggeln!
  mousecursor:=mousecursor;  //Mauscursor aktualisieren!
end;

procedure Tmainform.setmousecursor;
var
  hc:TCursor;
begin
  Fmousecursor:=value;
  if helpmode then hc:=crHelp
    else hc:=value;
  setallmousecursors(self,hc);
end;

procedure Tmainform.contexthelpbuttonClick(Sender: TObject);
  //Kontexthilfe auslösen mit Hilfe eines Kontext-Helpbutton (Togglebutton)
  //Muss entweder Speedbutton oder Toolbutton sein. Ggf. anpassen!
begin
  if sender is TToolButton then helpmode:=TToolbutton(sender).Down
    else
  if sender is TSpeedButton then helpmode:=TSpeedbutton(sender).Down;
end;

procedure Tmainform.Menu_ContextHelpClick(Sender: TObject);
  //Kontexthilfe auslösen vom MainMenu aus:
begin
  helpmode:=true;
end;

end.


Das wär's vorerst. Wenn noch Fragen offen sind, oder es Verbesserungswünsche gibt, dann bitte einfach hier posten. Ich bin für jeden Kommentar dankbar.

Gruß an alle Hilfesuchenden.
Einloggen, um Attachments anzusehen!
Logikmensch Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Do 13.04.06 06:15 
Ach, hier ist noch das Beispielprogramm.

Ich Dummerchen. :-)
Einloggen, um Attachments anzusehen!
elduchte
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 199

Win 2000, Win XP
Delphi 7
BeitragVerfasst: Fr 12.05.06 10:46 
Hallo,
erstmal vielen dank für dieses Tutorial.

Doch leider habe ich damit so meine Probleme:

a) ich kann das Bsp nicht starten.....
b) folgende Fehlermeldung wird bemängelt:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function CommonFormHelp(Command: Word; Data: Integer;
  var CallHelp: Boolean): Boolean;
  //Kann HLP- oder CHM-Hilfe aufrufen. Die Hilfedatei muss vorher in Application.Helpfile gesetzt worden sein
var
  ext:String;
begin
  ext:=uppercase(extractfileext(application.HelpFile));
  if ext='.HLP' then result:=winhelp(application.Handle,PChar(application.HelpFile),command,data)
    else
  if ext='.CHM' then result:=htmlhelp(application.Handle,PChar(application.HelpFile),HH_HELP_CONTEXT,data)<>0
    else result:=false;
  CallHelp:=result;
end;


Die Fehlermeldung lautet dann: undefinierter Bezeichner.... doch wo habe ich was vergessen ??

Danke
Logikmensch Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Di 16.05.06 06:34 
Hallo elduchte!

Also, bei meinem Delphi (2005) werden sowohl HTMLHelp als auch die HH.... - Konstanten durch die Unit Windows bereitgestellt. Selbiges gilt allerdings auch für die Funktion WinHelp, die bei Dir ja scheinbar funktioniert. Der eigentliche code ist offenbar in der Datei hhctrl.ocx enthalten, jedenfalls wird in meiner Windows.Pas diese Library beim Aufruf von HTMLHelp eingebunden. Leider weiß ich nicht, wie ich Dir jetzt helfen könnte... :'-( Mein Code läuft übrigens bei mir sowohl unter Win98SE als auch unter Win XP anstandsfrei.

Grüße vom Claus.