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:
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 |
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.
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 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.
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; 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.
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); 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; end else if ctl is TControl then begin with TControl(ctl) do if visible then cursor:=c; end; end; end; |
Die Funktion FINDHELPCONTEXT durchsucht ausgehend von der Mausposition PT die entsprechende Komponente und gibt deren HelpContext zurück.
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; 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; end else if ctl is TControl then begin with TControl(ctl) do if helpcontext<>0 then result:=helpcontext; end; end; end; |
Nachfolgend die Initialisierungen im FORMCREATE-Ereignis:
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; applicationpath:=extractfiledir(paramstr(0)); if (length(applicationpath)>0) and (applicationpath[length(applicationpath)]<>'\') then applicationpath:=applicationpath+'\'; 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.
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); var hc:integer; begin if helpmode and (msg.message=WM_LBUTTONDOWN) or (msg.message=WM_RBUTTONDOWN) then begin msg.message:=0; helpmode:=false; hc:=FindHelpContext(self,msg.pt); if hc<>0 then application.HelpContext(hc); handled:=true; end; end; |
Nachfolgend die Implementierungen der Properties HELPMODE und MOUSECURSOR.
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; begin result:=CommonFormHelp(command,data,callhelp); end;
procedure Tmainform.sethelpmode; begin Fhelpmode:=value; contexthelpbutton.Down:=value; mousecursor:=mousecursor; 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); 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); 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.