VOICE-Homepage: http://de.os2voice.org | Juni 2005 Inhaltsverzeichnis < Vorherige Seite | Nächste Seite > Artikelverzeichnis editor@os2voice.org

## DrDialog oder: Wie ich lernte, REXX zu lieben - Teil 14

Von Thomas Klein © Juni 2005

Nach einer langen, langen Pause (u.a. für meine Zertifikate in IT-Security und ITIL) habe ich endlich wieder Zeit gefunden, einen neuen Artikel unserer Serie über DrDialog zu schreiben. Es tut mir wirklich sehr leid, daß es so lange gedauert hat und möchte mich bei dieser Gelegenheit auch bei allen bedanken, die mich per E-Mail gefragt haben, wann denn 'mal ein neuer Artikel erscheint. Nun - hier ist er!

Was die Serie selbst betrifft, so glaube ich mich zu erinnern, daß ich eine kleine Beispielanwendung angekündigt hatte. Tja - ich habe beschlossen, diese Anwendung vorerst nicht zu realisieren. Ich muß den Tatsachen in's Auge sehen: Das Problem ist einfach die fehlende Zeit. Allerdings überlege ich, diese Beispielanwendung auf Basis eines gut dokumentierten Open-Source-Projekts zu realisieren. Aber das ist eine andere Geschichte... denn was diese Artikelserie hier bei VOICE betrifft, so erscheint es mir viel sinnvoller, Ihnen übliche Programmier-"tricks" für den Umgang mit REXX und DrDialog zu zeigen, weiterführende Techniken (wie z.B. Datenbankzugriff) zu besprechen oder nützliche Erweiterungen vorzustellen - wie beispielsweise gleich jetzt:

### Neue "controls" (Steuerelemente) zu DrDialog hinzufügen

Bisher haben wir bereits die Standard- und erweiterten Steuerelemente kennen gelernt, die uns DrDialog zur Verfügung stellt - wie push buttons, container, billboard etc. Außerdem haben wir die rexxutil library und die darin enthaltenen Funktionen besprochen. Dies zusammen stellt bereits ein beachtliches Arsenal an Werkzeugen zum Entwicklen von Anwendungen zur Verfügung. Aber manchmal ist es eben einfach nicht genug...
Sobald Ihr Programm einmal das macht, was es soll, können Sie damit fortfahren die Stabilität, Fehlerbehandlung oder auch die Performance zu verbessern. Oder Sie könnten auch den Leistungsumfang erweitern. Und natürlich wäre es interessant, eine Online-Hilfe einzubauen, um die Benutzerfreundlichkeit zu steigern. Doch an irgend einem Punkt stellt man schließlich fest, daß es im Grunde trotz allem immer noch verdammt häßlich aussieht.
Nun gut - was das Thema Online-Hilfe angeht, muß ich zugeben, daß ich mich damit noch nie selber beschäftigt habe. Meine eigenen "Schnellschuß"-Lösungen haben es nie bis zu einem Status gebracht, in dem eine Online-Hilfe sinnvoll gewesen wäre. Meistens waren die kleinen Programme selbsterklärend... zumindest für mich. Und da sie nie dazu bestimmt waren, das Licht der Öffentlichkeit zu erblicken, bestand keine Notwendigkeit für Benutzerfreundlichkeit. Wenn also jemand in der Lage wäre, die Verwendung von Online-Hilfe in DrDialog zu beschreiben oder zu erklären, dann nur zu - die VOICE-Redakteure laden Sie ein! Und ich wäre sehr erfreut, wenn ich nicht zuerst das Ganze von Null auf zu begreifen hätte, um danach darüber zu schreiben. ;)

Aber wo wir gerade über häßliche Programme reden - was tun? Wie wäre es zum Beispiel mit einer Fortschrittsanzeige (alias "Prozentbalken"), um langwierige Verarbeitungssequenzen zu überbrücken? Wie wär's mit tooltips? Und falls Ihr Programm mit Bilddateien zu tun hat - warum nicht eine Voranzeige der Bilder ermöglichen, anstatt diese lediglich über deren Dateinamen zu verwalten? Dazu könnte ich Ihnen etwas sagen. Und zwar gleich jetzt.

Wenn Sie ein "zeitgemäßer Warper" sind, haben Sie wahrscheinlich schon einmal den Namen Chris Wohlgemuth gehört. Abgesehen vom seinen großartigen CDR-tools, den CWUSB-Treibern und dem WPS-Wizard bescherte er uns auch eine Sammlung von controls und Funktionen, die den bereits großen Leistungsumfang von DrDialog noch erweitern. Seitdem ich diese Werkzeuge das erste mal benutzt habe, bin ich fest davon überzeugt, daß sie ein absolutes "Muß" für alle Entwickler sind, die DrDialog einsetzen.

### Wie Erweiterungssteuerelemente in DrDialog funktionieren

Damit wir uns gleich richtig verstehen: Ich habe nie auch nur eine einzige solche Erweiterung selber entwickelt. Was ich hier beschreibe, basiert ausschließlich auf meinen Erfahrungen als Entwickler in anderen Bereichen, meiner Vorstellung davon, wie die Sachen zusammenhängen und geschieht nach bestem Wissen und Gewissen. Gut - legen wir also los. Als Dave Morrill DrDialog entwickelte, stellte er ein so genanntes "user control" (Benutzersteuerelement) bereit. Wenn Sie schon einmal versucht haben, es zu verwenden, ohne zu wissen, um was es dabei geht (also so wie ich, bevor ich Chris' Sammlung kannte), haben Sie bestimmt bemerkt, daß nicht wirklich viel passiert: Nachdem man das control auf einem Dialog ablegt hat, kann man eigentlich nix damit anfangen. Hm. Doofes Teil.

Der Grund dafür ist, daß es sich bei dem kleinen Juwel um eine Art "universellen Platzhalter für Erweiterungen" handelt. Das Problem ist nämlich, daß DrDialog von Hause aus nicht über die Mittel verfügt, um Erweiterungen "nahtlos" in seine Entwicklungsumgebung aufzunehmen (wie es bspw. bei VisualBasic der Fall ist). Für uns bedeutet das, daß ein user control also für alle möglichen controls steht, die nicht bereits in DrDialog eingebaut sind.
Damit eine Erweiterung mit DrDialog verwendet werden kann, muß sie bestimmte Mechanismen beherrschen, die eine Kommunikation zwischen der Ausführungsumgebung von DrDialog und der Erweiterung selbst ermöglichen. Die Verbindung zwischen den beiden Welten wird durch das user control erreicht:
Auf der einen Seite fügt es sich in die Nachrichten- und Ereignissteuerung von DrDialog ein wie jedes andere control. Auf der anderen Seite stellt es standardisierte Schnittstellen zur Verfügung, die eine Reaktion auf Ereignisse und die Kommunikation mit dem DrDialog-System ermöglichen.

### Chris' control- und Funktionsbibliothek

Zunächst hier deren Inhalt in aller Kürze (ein übersetzter Auszug aus der Datei `FILE_ID.DIZ` der Version 0.1.5):

- Fortschrittsanzeige-Steuerelement für DrDialog mit einstellbarer Farbe, Schriftart und Anzeige.
- Maussensitives Hilfefenster ("tooltip") für DrDialog mit einstellbarer Farbe, Schriftart, Verzögerung usw.
- Bild-Steuerelement für DrDialog, das jedes von OS/2 beherrschten Bilddateiformat anzeigen kann, mit optionaler Funktion für Bereichsauswahl.
- Verzeichnisauswahldialog
- Funktionen zur Steuerung des Vater<->Kind-Verhaltens von Dialogen
- Histogramm-Steuerelement für Bilddateien

Boah - ziemlich viel. Und das alles in einer einzigen DLL.
Keine Sorge, falls Sie den einen oder anderen der aufgezählten Punkte nicht verstehen - wir besprechen sie im Folgenden.
Zusätzlich zum mitgelieferten Quellcode der DLL hat Chris auch noch Beispielanwendungen hinzugepackt, um Aussehen, Verhalten und Verwendung der Steuerelemente und Funktionen zu demonstrieren. Das komplette Paket ist erhältlich in Form einer ZIP-Datei, die... ähh, Moment - ich fang' nur schnell einen neuen Absatz an:

### Woher man's bekommt

Die fragliche Datei, nach der Sie Ausschau halten müssen, ist die Version 0.1.5. deren offizieller Dateiname `drdialog-controls-0_1_5.zip` lautet. Momentan ist (war) diese auf Chris' Webseite(n) erhältlich unter den beiden folgenden Adressen:

- http://www.geocities.com/SiliconValley/Sector/5785/
- http://www.os2world.com/cdwriting

Folgen Sie den jeweils entsprechenden Links auf den Seiten, laden Sie die Datei herunter und entpacken Sie sie in ein (vorzugsweise leeres) Verzeichnis.

### Wie man's installiert

Tatsächlich benötigen Sie aus der .zip-Datei eigentlich nur die Datei `drctl015.dll`. Auf jeden Fall sollten Sie zunächst sicherstellen, daß diese Datei sowohl aus Ihrer DrDialog-Umgebung ladbar ist als auch von Ihren compilierten Programmen, die sich unter Umständen in anderen Verzeichnissen befinden. Am einfachsten erreichen Sie das, indem die Datei in einem Verzeichnis abgelegt wird, welches Bestandteil des `LIBPATH` ist (was ich ungeachtet der Umstände grundsätzlich empfehlen würde).

Hintergrund: Wenn Sie die DLL im Installationsverzeichnis von DrDialog ablegen, ist alles in Ordnung, solange Ihre Programme aus der DrDialog-Entwicklungsumgebung heraus gestartet werden. Zu diesem Zeitpunkt ist es nämlich `DRREXX.EXE` (das Laufzeitmodul der Entwicklungsumgebung) welches die DLL lädt. Da die meisten Entwickler jedoch (wie ich) nicht nur Ihre Quellcodes sondern auch die Kompilate (lauffähige, übersetze Programme) vorzugsweise in davon getrennten Verzeichnissen ablegen, würde das Starten eines übersetzten Programms mit einem Fehler enden. Warum? Weil es nun keine DrDialog-Umgebung mehr gibt und das aktuelle Arbeitsverzeichnis nun nicht mehr das mit der DLL ist, sondern das Verzeichnis, in dem sich das ausführbare Programm befindet. OS/2 findet die benötigte DLL nun weder im aktuellen Arbeitsverzeichnis noch in einem der `LIBPATH`-Verzeichnisse. Peng, Fehler. (Außer Sie machen das DrDialog-Verzeichnis zu einem Bestandteil des `LIBPATH`, aber - hallo - wozu das?)

Um diese Probleme einfach zu umgehen und darüber hinaus auch nicht doppelte oder mehrfach vorhandene DLLs auf Ihrer Festplatte zu verteilen (und dann vielleicht noch unterschiedliche Versionen davon!) legen Sie die DLL einfach in dem Verzeichnis ab, wo sich auch die anderen DLLs für REXX befinden. Ich für meinen Teil lege solche DLLs immer im selben Verzeichnis ab, wo sich auch `REXXUTIL.DLL` befindet - standardmäßig sollte das `\OS2\DLL` sein, welches (auch standardmäßig) Bestandteil des `LIBPATH` ist.

Um die Beispielprogramme zu testen, könnten Sie für diese noch noch ein eigenes (Unter-) Verzeichnis anlegen und die Dateien dorthin verschieben. Wenn Sie beispielsweise bereits über andere Demoprogramme verfügen, wäre deren Speicherort eine gute Stelle dafür. Aber das bleibt Ihnen überlassen. Sofern Sie nicht darauf brennen, die neuen Funktionen und Steuerelemente sofort in eigene Anwendungen einzubauen, sollten Sie sich Chris' Beispielprogramme kurz zu Gemüte führen. Außerdem können Sie auf diese Weise direkt sicherstellen, daß Ihre 'Installation' bezüglich der DLL korrekt ist:
Starten Sie DrDialog und öffnen Sie die Datei `DEMO.RES`, die Bestandteil der ZIP-Datei war. Schauen Sie sich erst gar nicht lange alles im Detail an - starten Sie nun einfach das Programm sofort aus DrDialog heraus. Cool. Dieses Beispiel veranschaulicht auf einen Schlag sowohl die Steuerelemente Fortschrittsanzeige, Tooltips, Image als auch die Funktion zur Verzeichnisauswahl.
Das Besondere am Image-Steuerelement (im Vergleich zu DrDialogs Billboard usw.) ist die Tatsache, daß, während DrDialog nur mit BMP-Dateien umgehen kann, Chris' Steuerelement jeden Bildtyp verwenden kann, für den OS/2 über eine entsprechende I/O-Proc (Ein-/Ausgabeprozedur) verfügt - also `BMP, TIF, GIF` und sogar `JPEG` sofern Sie mit Ihrem OS/2 oder eCS zumindest die `Basisunterstützung für Multimedia` installiert haben.

Ein kleiner Hinweis, falls Sie es noch nicht wußten:
Der Umgang mit `JPG`-Dateien liegt technisch gesehen in der Verantwortung von MMPM/2 - und nicht "OS/2 selbst". Damit Sie also JPEG-Dateien direkt in OS/2 verwenden können (beispielsweise als Hintergrund für Ordner der Arbeitsoberfläche), müssen Sie MMPM/2 installiert haben - auch, wenn Sie keine Soundausgabe haben bzw. möchten. Dies betrifft allerdings nicht Grafikprogramme (wie z.B. Embellish) da diese für gewöhnlich ihre eigenen Routinen für solche Zwecke haben und sich dafür nicht auf das Betriebssystem verlassen müssen.
Wenn Sie hin und wieder "Probleme" mit einzelnen .jpg Dateien in Ihrem (MM)OS/2 bemerken, sollten Sie probeweise 'mal das JPEGIO package von Chris' Webseite installieren: Hierbei handelt es sich um eine erweiterte Klassenbibliothek für MMPM/2, die auch die neueren JPG-Typen mit "progressiver Codierung" unterstützt, die von der ursprünglichen OS/2-Klasse nicht "verstanden" werden. Okay, genug jetzt - kommen wir zurück zu den Steuerelementen.

### Wie man's verwendet

Nun, da wir die Erweiterungsbibliothek heruntergeladen, installiert und uns (vielleicht) auch schon die erste Beispielanwendung angeschaut haben, werden wir uns damit beschäftigen, wie die neuen Steuerelemente und Funktionen in eigenen Programmen verwendet werden und was es dabei zu beachten gilt. Zunächst rufen wir uns nochmals in Erinnerung, daß in der DLL sowohl Steuerelemente und die damit verbundenen Funktionen enthalten sind, als auch Funktionen, die nicht mit einem konkreten Steuerelement "verbunden" sind - wie beispielsweise die Verzeichnisauswahl.

#### 1. Funktionsbibliothek laden

Wie wir bereits von anderen "Erweiterungsbibliotheken" wie `rexxutil` wissen, müssen wir eine solche Bibliothek zunächst immer "laden", um deren Inhalte verwenden zu können. Bei Chris' Bibliothek verhält sich das nicht anders - mit der Ausnahme, daß hier nicht nur Funktionen für REXX enthalten sind, sondern auch Steuerelemente für DrDialog. Daher können bzw. müssen Sie die zu verwendende Komponente separat laden:
Wenn Sie beabsichtigen, eines, mehrere oder alle der Steuerelemente (Fortschrittsanzeige, Bildanzeige, Histogramm oder Tooltip) zu verwenden, müssen Sie die Steuerelemente registrieren.

>     /* Steuerelemente aus Chris' Bibliothek laden */
>     rc=RxFuncAdd("DRCtrlRegister", "drctl015", "DRCtrlRegister")
>     call DRCtrlRegister

Wollen Sie eine, mehrere oder alle der enthaltenen Funktion verwenden (also die, die nicht an ein Steuerelement "gebunden" sind, wie z.B. die Verzeichnisauswahl) dann müssen Sie die Funktionen laden. Sie können hier wahlweise eine einzelne Funktion laden oder auch alle in der Bibliothek enthaltenen Funktionen "in einem Rutsch". Und wissen Sie was? Ich würde hier "KISS" empfehlen.

Hä? Kiss? Genau: "(K)eep (I)t (S)imple and (S)tupid" - sozusagen "Dumm und einfach halten!"
Laden Sie einfach immer alle Funktionen und scheren Sie sich nicht um Details - basta...

>     /* Alle Funktionen aus Chris' Bibliothek laden */
>     rc=RxFuncAdd("DRCtrlLoadFuncs", "drctl015","DRCtrlLoadFuncs")
>     call DRCtrlLoadFuncs

Wir rekapitulieren also:

- Um Steuerelemente zu verwenden: `DRCtrlRegister` aufrufen.
- Um Funktionen zu verwenden: `DRCtrlLoadFuncs` aufrufen

Und nun die Preisfrage...: Was ruft man auf, wenn man Steuerelemente und Funktionen verwenden möchte?
Na...? Genau: Beides!

>     /* Alles aus Chris' Bibliothek laden */
>     rc=RxFuncAdd("DRCtrlRegister", "drctl015", "DRCtrlRegister")
>     call DRCtrlRegister
>     rc=RxFuncAdd("DRCtrlLoadFuncs", "drctl015","DRCtrlLoadFuncs")
>     call DRCtrlLoadFuncs

Und wo wir gerade dabei sind:
Angeblich lädt doch `DrCtrlLoadFuncs` alle Funktionen. Folglich lädt es auch schon `DrCtrlRegister` - richtig?
Richtig! Na, dann lassen Sie uns doch den vorherigen Abschnitt 'mal etwas straffen zu...:

>     /* Alles aus Chris' Bibliothek laden - Kurzfassung */
>     rc=RxFuncAdd("DRCtrlLoadFuncs", "drctl015" ,"DRCtrlLoadFuncs")
>     call DRCtrlLoadFuncs
>     call DRCtrlRegister

Aha. So weit, so gut. Und wo im Programm macht man das jetzt?
Das hängt davon ab, welche der Startmethoden von DrDialog Sie sich zunutze machen. Erinnern Sie sich, daß die Ausführungssteuerung von DrDialog zwei Methoden beherrscht, wie ein Programm gestartet wird? Hier eine kurze Rückblende dazu:

1. Eine globale Routine mit dem Namen `INIT`.
   Ich bevorzuge dieses Verfahren, da es mir ermöglicht, Befehle auszuführen, bevor noch irgendein Dialog geladen wird. Außerdem ermöglicht es mir, einen Dialog meiner Wahl als ersten anzuzeigen (sofern mehr als ein Dialog vorhanden ist).

2. Falls keine solche Prozedur existiert, startet DrDialog automatisch mit dem Dialog, der die kleinste ID-Nummer hat (standardmäßig ist das die ID 100). Außerdem wird, falls in der Prozedur `INIT` kein anderer Dialog per code gestartet wird, nach deren Abarbeitung ebenfalls automatisch der erste Dialog (mit der kleinsten ID) geladen.

Obwohl nichts dagegen spricht, das Laden der Bibliothek innerhalb eines Eventhandlers durchzuführen (wie bspw. im `INIT-Ereignis` eines Dialogs), empfehle ich, es stattdessen in der globalen `INIT-Prozedur` durchzuführen. Das ist in erster Linie aus "logischen" Gründen: Theoretisch kann eine Eventhandler-Routine eines Steuerelements - also auch eines Dialogs - im "Lebenszyklus" eines Programms mehr als einmal ausgeführt werden. Dies trifft auch auf das `INIT-Ereignis` eines Dialogs zu. Warum? Weil es möglich ist - und gerade bei der Verwendung von mehreren Dialogen auch erforderlich - diese aus dem code heraus zu laden, zu beenden und erneut zu laden. Somit kann also auch das `INIT-Ereignis` mehr als einmal auftreten und das Laden der Bibliothek entsprechend mehr als einmal durchgeführt werden.

Gut - um ehrlich zu sein, habe ich gerade 'mal schnell getestet, was passiert, wenn man die Bibliothek mehr als einmal lädt: Nichts.
Kein Absturz, kein Aufhängen, kein Fehler. Nun gut... aber lassen Sie mich darauf hinweisen, daß während REXX und DrDialog Ihnen das mehrfache Laden der selben DLL vergeben, ich es Ihnen nicht vergebe! ;-) Ich meine... hey... genau da fängt doch schlampiges Programmieren an:
Bedenken Sie bitte, daß das Laden einer Bibliothek das Betriebssystem dazu veranlaßt "irgendetwas" zu tun. Theoretisch muß dieses Etwas nur einmal gemacht werden - warum es also mehrfach tun, nur weil wir zu "faul" sind, uns mit den Auswirkungen zu beschäftigen?
Tja - in einer perfekten Welt würde man eine globale `INIT-Prozedur` anlegen, darin das Laden der Bibliothek durchführen und "gut ist". Aber das Leben ist nun einmal nicht perfekt und meist ist die Theorie weit von der Praxis entfernt. Oder zumindest sind die Kosten für Perfektion einfach zu hoch. Obwohl ich Sie also nach wie vor beschwöre, sich mit den Grundsätzen der globalen `INIT-Prozedur` auseinanderzusetzen, um das Steuerungskonzept von DrDialog zu verstehen (und es im vollem nutzen zu können), drücke ich auch ein Auge zu, wenn Sie die Bibliothek aus einem Dialogereignis heraus starten (z.B. weil es nur einen Dialog gibt). [seufz]

#### 2. Verwenden der Verzeichnisauswahl

Man kann es drehen und wenden wie man will - letztendlich beschäftigen sich Programme meistens mit Dateien.
Sofern der Anwender eine existierende Eingabedatei auswählen oder eine Ausgabedatei angeben soll, läßt sich das in DrDialog relativ leicht mit den Standard-Dateidialogen des Betriebssystems erreichen.
(Da fällt mir gerade siedendheiß ein: ...haben wir das eigentlich schon besprochen? Gut, wir holen das bei Gelegenheit nach...)

Leider möchte man aber manchmal, daß der Anwender nur ein Verzeichnis angibt, weil das Programm selbst sich um "den Rest" kümmert. Zum Beispiel: Die Konfigurationsdatei Ihres Programm heißt grundsätzlich `MeinProg.INI` und Sie möchten vom Anwender lediglich wissen, "Wo soll die Konfigurationsdatei gespeichert werden?".
In solchen Fällen kann es mühsam werden, dieses Verhalten mit Hilfe der Standarddialoge zu realisieren, da diese dem Anwendern nicht nur gestatten, ein Laufwerk und Verzeichnis auszuwählen, sondern auch einen Dateinamen anzugeben. Was auch immer von diesen Dialogen zurückgeliefert wird muß also zunächst "validiert" werden, um es von einem eventuell angegebenen Dateinamen zu befreien und "nur" Laufwerk- und Verzeichnisangabe zu erhalten. Ganz zu schweigen vom "Verwirrungseffekt": Der Anwender soll ein Verzeichnis angeben aber es erscheint ein Dialog zur Angabe von Dateien. Da liegt dann natürlich die Frage des Anwenders nahe, wie sich ein (nicht?) eingegebener Dateiname auf das Programm auswirkt... Sie sehen: Das paßt irgendwie nicht.
Wenn Sie bereits einen eigenen "Verzeichnisauswahldialog" programmiert haben, dann werden Sie diesen garantiert auch in neuen Programm verwenden wollen. Super. Wenn Sie aber in Ihrem Code-Repertoire keinen solchen Dialog haben und nicht unbedingt einen Dialog benötigen, der sich 100%-ig am Aussehen und Verhalten Ihres Programms orientiert (oder Ihnen das egal ist), dann können Sie sich erhebliche Arbeit sparen durch Verwendung von Chris' Verzeichnisdialog.

Der Verzeichnisdialog wird als simpler Funktionsaufruf bereitgestellt: Man übergibt ihr einen Anfangspfad sowie Dialogtitel und erhält nach Beendigung eine Zeichenkette, die entweder leer ist (wenn der Anwender den Dialog abgebrochen hat) oder das ausgewählte Verzeichnis inklusive Laufwerksbuchstaben enthält. Sofern Sie nicht bereits (Stichwort "DrCtrlLoadFuncs") die Funktionen im vollen Umfang geladen haben und lediglich den Verzeichnisdialog benötigen, können/müssen Sie diesen mit dem folgenden Befehl laden:

>     rc=RxFuncAdd("DRCtrlPickDirectory", "drctl015" ,"DRCtrlPickDirectory")

Sobald die Funktion durch die eine oder andere Weise bereitgestellt wurde, starten Sie sie mit folgendem Aufruf:

>     DasVerzeichnis = DRCtrlPickDirectory('c:\temp','Verzeichnis für temporäre Dateien wählen')

Zur Laufzeit erscheint damit der Verzeichnisdialog und es erfolgt keine Rückkehr zu Ihrem Programmcode, bis der Anwender entweder den Dialog abgebrochen oder ein Verzeichnis ausgewählt hat. Um festzustellen, auf welche dieser beiden Wege der Dialog beendet wurde, prüfen Sie einfach den Inhalt der zurückgegebenen Zeichenkette:

>     DasVerzeichnis = DRCtrlPickDirectory('c:\temp','Verzeichnis für temporäre Dateien wählen')
>     if DasVerzeichnis = '' then
>       say 'Dialog wurde abgebrochen'
>     else
>       say 'Es wurde gewählt: ' || DasVerzeichnis

In der Praxis ist dieses Beispiel natürlich relativ sinnlos - aber es gibt Ihnen zumindest einen Eindruck davon, wie die Funktion der Verzeichnisauswahl und des Dialoges in Ihren eigenen Anwendungen integriert werden kann.

Die Frage, die sich in Zusammenhang mit der Verzeichnisauswahl wohl am ehesten ergibt, ist:

> Was passiert, wenn das im Aufruf übergebene Verzeichnis (das "Anfangsverzeichnis") nicht existiert?

Nun - Chris' Verzeichnisauswahl ist fast schon "idiotensicher": Wenn das übergebene Verzeichnis nicht existiert, wird einfach das Arbeitsverzeichnis verwendet, welches auch immer das jeweils ist. Zumindest findet man sich nicht plötzlich in einer unvorhersehbaren Situation. Wenn sich das übergebene Verzeichnis auf einem Laufwerk befindet, das gerade nicht bereit sein sollte oder nicht (mehr) existiert, wie z.B. ein getrenntes Netzwerklaufwerk oder ein nicht bereites Wechsellaufwerk, erscheint eine Fehlermeldung "Laufwerk nicht bereit", nach deren Bestätigung dann die Verzeichnisauswahl erscheint... es ist also - wie ich bereits sagte - nicht allzu unvorhersehbar.

Das wär's auch schon zur Verzeichnisauswahl. Und nein:
Sie brauchen die Funktion nicht zu entladen... es gibt im Übrigen auch keine Funktion dafür, es sei denn Sie entladen die komplette Bibliothek... was aber auch nicht nötig ist. Also vergessen wir das einfach... :-)

#### 3. Verwenden der Fortschrittsanzeige

Nun kommen wir zu den Erweiterungen aus Chris' Bibliothek, die sich um die Benutzerschnittstelle drehen. Als erstes Steuerelement schauen wir uns die Fortschrittsanzeige (oder "Prozentbalken") an. Wie bereits erwähnt, laden wir erst einmal die entsprechenden Bestandteile der Bibliothek:

>     rc=RxFuncAdd("DRCtrlRegister", "drctl015" ,"DRCtrlRegister")
>     call DRCtrlRegister

So weit so gut, aber... woher zum Teufel kriegt man jetzt eine Fortschrittsanzeige auf den Dialog gezogen? In der Sammlung der Steuerelemente zur Entwurfszeit gibt es nach wie vor keine "Fortschrittsanzeige"! Moment! Was war das noch... ähm... ach ja: User control! Ein benutzerdefiniertes Steuerelement.

Ziehen Sie ein user control auf Ihren Dialog, lassen es an der gewünschten Stelle fallen und stellen Sie die gewünschten Abmessungen ein.
Klicken Sie nun mit Maustaste 2 darauf und wählen Sie aus dem Menü den Punkt Style.

Im Eingabefeld class ("Klasse") schreiben Sie dann DRD_PERCENTBAR und drücken `<Eingabe>` oder (oder klicken Sie OK).

Das wäre schon alles, was die Dialoggestaltung zur Entwurfszeit angeht.

Hm. Nett - aber wie stellt man jetzt die Farben oder andere Eigenschaften ein?
Per code! Man muß mit dem Steuerelement "kommunizieren" und dafür die entsprechenden Funktionen der Bibliothek verwenden. Das bedeutet, daß Sie beim Starten aus DrDialog heraus nichts am Bildschirm sehen werden, bevor Sie es nicht explizit im code mitteilen. Zwangsläufig werden wir uns also nun anschauen, was/wie man das macht. Insgesamt stehen uns zwar nur drei Funktionen für den Umgang mit der Fortschrittsanzeige zur Verfügung, aber sie reichen völlig aus:

- `color`
- `text`
- `font`

Damit Ihnen alle Informationen an einer Stelle zur Verfügung stehen und ich Sie nicht auf die DrDialog-Hilfe verweisen muß, werde ich jetzt eine kurze Zusammenfassung der `color` und `font` Funktion bzw. Anweisung zum Besten geben (die Funktion bzw. Anweisung `text` erfordert eine etwas ausführlichere Behandlung, daher nehmen wir sie uns erst weiter unten vor).

`Color` wird in zwei aufeinander folgenden Aufrufen verwendet, um

- die Hintergrundfarbe einzustellen (was der Farbe des Prozent"balkens" entspricht) und
- die Vordergrundfarbe einzustellen (die Farbe der Schrift, die "auf dem Balken" erscheint)

Farben werden nach dem RGB-Verfahren angegeben. Dabei handelt es sich um eine Reihe von drei Werten die jeweils zwischen 0 und 255 liegen und jeweils den Farbanteil von (R)ot, (G)rün und (B)lau der gewünschten Farbe angeben. Schwarz ist dabei gleichbedeutend mit 0 (Null) für alle drei Werte; der Maximalwert 255 für alle drei Werte entspricht Weiß. Zusätzlich müssen Sie beim Aufruf noch angeben, ob Sie sich auf die Vordergrundfarbe (Parameter = "+") oder die Hintergrundfarbe ("-") beziehen. Hier ein Beispiel:

>     call color "-", "#255 255 255"

>     call color "+", "#0 0 255"

Der obige code (vorzugsweise zu verwenden im `INIT-Ereignishandler` der Fortschrittsanzeige - pardon, des user control) stellt die Farbe des Balkens auf Weiß und den Text darauf auf Blau. Um die gewünschten Farbwerte einer bestimmten Farbe zu ermitteln, verwenden Sie einfach die Farbmischanzeige der Farbpalette oder ein beliebiges Grafikprogramm (jedes mir bekannte Programm bietet ein entsprechendes Werkzeug).

Die Einstellung der für die Anzeige verwendeten Schriftart jedoch ist relativ simpel, obwohl es dabei gilt, eine bestimmte Notation zu verwenden. Im Prinzip wird die Punktgröße der Schriftart angegeben, dann ein Punkt sowie der Namen der gewünschten Schriftart. Und das alles als eine einzelne Zeichenkette wie in

>     call font "8.Helv"

um für die Textanzeige die Schriftart `Helv` in der Punktgröße 8 zu wählen.
Tipp: Um den "korrekten" Namen einer Schriftart zu ermitteln, verwenden Sie einfach die Schriftartpalette.

Während `color` und `font` also genau wie bei jedem anderen Steuerelement von DrDialog verwendet werden, muß die Funktion `text` eine bestimmte Syntax verwenden, da damit gleichzeitig der Prozentwert der Anzeige als auch der darauf angezeigte Text eingestellt wird. Im Prinzip lautet die Syntax:

>     <prozentwert> [#text]

Was bedeutet, daß der Prozentwert (z.B. '50' für fünfzig Prozent) eine Pflichtangabe ist während der Text eine optionale Angabe darstellt. Wenn Sie einen Text mitangeben wollen, so muß dieser vom Prozentwert durch ein "hash"-Zeichen ("#") getrennt werden. Um also beispielsweise eine leere Prozentanzeige mit dem Text "Initialisierung..." anzuzeigen, würden Sie das wie folgt codieren:

>     call text '0#Initialisierung...'

Um den Balken der Fortschrittsanzeige auf 50 Prozent zu setzen und den Text "50%" darauf anzuzeigen, verwenden Sie:

>     call text '50#50%'

Ach ja - wo wir gerade über "Initial"-Werte reden:
Man könnte dem Verdacht erliegen, daß Farbe, Schriftart und Text sich auch über das Kontextmenü des user control zur Entwurfszeit einstellen lassen - so wie bei den anderen Steuerelementen auch. Aber das ist falsch: Wenn Sie das Kontextmenü aufrufen, werden Sie feststellen, daß Font und Text dort zwar enthalten sind (Color nicht), deren Verwendung aber keinen Einfluß darauf hat, was nach dem Laden und Anzeigen des Dialogs tatsächlich erscheint. Und das auch nicht zur Laufzeit. Wir notieren also:

> Text, Color und Font des Benutzersteuerelements "Fortschrittsanzeige" können nur zur Laufzeit per code gesetzt werden!

Und noch ein Hinweis:
Denken Sie immer daran, die Namen der Steuerelemente voll-qualifiziert anzugeben, wenn Sie deren Eigenschaften abfragen oder einstellen!
Normalerweise wird man die Fortschrittsanzeige aus der Prozedur heraus steuern, wo auch die eigentliche Verarbeitung stattfindet. Stellen Sie sich zum Beispiel eine Adreßbuchanwendung vor: Wenn der Anwender das Speichern aller Kontakte in eine Datei auswählt, soll dies mittels einer Fortschrittsanzeige bildlich untermalt werden. Um dies zu erreichen, würde man eine Routine aufrufen, welche

- die Gesamtanzahl der Kontakte ermittelt
- diese Kontakte dann einzeln nacheinander speichert
- die prozentuelle Erledigung des Vorgangs nach jedem gespeicherten Kontakt berechnet
  (`anz_kontakte_gespeichert * 100 / anz_kontakte_gesamt` )

Um nun aber die Fortschrittsanzeige aus einer solchen Prozedur bzw. Funktion heraus einstellen zu können, müssen Sie DrDialog mitteilen, auf welches Steuerelement Sie sich beziehen - indem Sie voll-qualifizierte Namen verwenden wie z.B. hier:

>     <dialog>.<steuerelement>.<eigenschaft> = <wert>

Das ist die Notation, die man grundsätzlich verwenden sollte. Zu einem späteren Zeitpunkt werden wir uns einmal mit der "dynamischen" Adressierung von Steuerelementen beschäftigen - und hier ist diese Form der Notation ein absolutes Muß. Für's erste reicht es aber auch, wenn Sie wissen, daß DrDialog auch das Benennen von Steuerelementen ohne Dialogname unterstützt. Demnach funktioniert

>     <steuerelement>.<eigenschaft> = <wert>

ebenso (im Gegensatz zu anderen Entwicklungsumgebungen). Im Prinzip liegt das daran, daß innerhalb DrDialog alle Dialoge und Steuerelemente "global" sind, da sie sich innerhalb der DrDialog-Umgebung befinden. Beispiel: Wenn Sie eine Schaltfläche namens btnExit in einem Dialog verwenden, können Sie kein anderes Steuerelement genau so nennen - auch nicht, wenn es sich in einem anderen Dialog desselben Projekts befindet.

Angenommen, der Name Ihres Fortschrittsanzeige-Steuerelements lautet prcnt_saved und es ist Bestandteil eines Dialogs namens dlg_main, dann würde ein Funktionsaufruf zur Initialisierung desselben wie folgt aussehen:

>     call dlg_main.prcnt_saved.text "0#Initialisierung..."

Oder eben so:

>     call prcnt_saved.text "0#Initialisierung..."

Aber das hier:

>     call text "0#Initialisierung..."

würde nur funktionieren, wenn es aus einer Routine heraus aufgerufen würde, die unmittelbar zum Fortschrittsanzeige-Steuerelement gehört (bspw. die Ereignisroutine `INIT`), aber nicht aus einer globalen Funktion oder einer Routine, die zu einem anderen Steuerelement gehört. Gut. Zurück zum Thema:

Wie sich wahrscheinlich bereits ahnen, bedeutet das Setzen der Prozentanzeige also das "zusammenbasteln" von Zeichenketten, da man jeweils aktuell die Werte an das Steuerelement übergeben muß und diese sowohl den Prozentwert als auch den anzuzeigenden Text enthalten. Beachten Sie, daß die eigentliche Berechnung der Prozente Ihnen obliegt! Wir könnten ja auch kurz ein kleines Beispielprogramm entwickeln... dazu benötigen wir:

- eine Fortschrittsanzeige natürlich!
- eine "start"-Schaltfläche
- etwas code ;)

Schritt 1:

> Beginnen Sie ein neues Projekt/Programm in DrDialog. Sie erhalten dann einen leeren Dialog. Nennen Sie diesen dlgHauptprogramm indem Sie aus dessen Kontextmenü den Eintrag name auswählen.

Schritt 2:

> Ziehen Sie eine Schaltfläche (pushbutton) aus der Steuerelementesammlung auf eine Stelle Ihrer Wahl im Dialog und benennen Sie sie als btnStart .

Schritt 3:

> Ziehen Sie dann ein Benutzersteuerelement (user control) auf Ihren Dialog, nennen Sie es przVerarb und stellen dessen Größe so ein, daß es einem schmalen, horizontalen Balken entspricht. Aus dem Kontextmenü des Steuerelements wählen Sie den Eintrag style und geben Sie im Feld class ein: DRD_PERCENTBAR

Schritt 4:

> Legen Sie eine globale Routine mit dem Namen "INIT" an (natürlich ohne die Anführungszeichen) und fügen Sie die folgende Anweisungen darin ein:

>     rc=RxFuncAdd("DRCtrlRegister", "drctl015","DRCtrlRegister")
>     call DRCtrlRegister

Schritt 5:

> In der Ereignisroutine INIT des Benutzersteuerelements (przVerarb) fügen Sie ein:

>     call text "0"
>     call color "-", "#0 0 255"
>     call color "+", "#255 255 255"

Schritt 6:

> In der CLICK-Ereignisroutine der Schaltfläche codieren Sie nun:

>     call Verarbeitung

Schritt 7:

> Legen Sie eine globale Routine mit dem Namen "Verarbeitung" an und fügen Sie darin die folgenden Anweisungen ein:

>     call btnStart.disable

>     maxval = 205
>     currprc=0
>     oldprc=0

>     do i = 1 to maxval
>       currprc = trunc(i * 100 / maxval)
>       if currprc \= oldprc then
>         do
>           call dlgHauptprogramm.przVerarb.text currprc || "#" || currprc || "%"
>           oldprc = currprc
>         end
>         call sleep 10
>       end

>     call sleep 1000

>     do i = maxval to 1 by -1
>       currprc = trunc(i * 100 / maxval)
>       call dlgHauptprogramm.przVerarb.text currprc || "#" || i || " Schritte übrig"
>       call sleep 5
>     end

>     call dlgHauptprogramm.przVerarb.text "0#demo completed!"

>     call dlgHauptprogramm.btnStart.enable

Schritt 8:

> Laufen lassen - anschauen.

Das Programm demonstriert eine Kombination dessen, was mit dem Fortschrittsanzeige-Steuerelement machbar ist. Im ersten Programmteil wird lediglich eine Schleife durchlaufen, in der eine Variable vom Wert 1 um 1 erhöht wird, bis der Maximalwert von 205 erreicht ist. Bei jedem Durchlauf der Schleife wird der Prozentwert der "Erreichung" neu berechnet.
Sobald der errechnete Prozentwert vom momentan angezeigten Prozentwert abweicht, wird die Fortschrittsanzeige aktualisiert und der neue Prozentwert in den Vergleichswert übernommen (für kommenden Vergleiche).

> Stopp, Moment! Wozu dieses Vergleichen?
> Weil man nicht bei jedem Durchlauf die Fortschrittsanzeige aktualisieren will, obwohl der Prozentwert sich (noch) gar nicht verändert hat, oder?

Nach diesem ersten Programmteil erfolgt eine zweisekündige Pause.
Dann, im zweiten Teil, wird die Fortschrittsanzeige "rückwärts" verwendet. Und diesmal zeigen wir keine Prozentwerte darauf an, sondern vielmehr den internen Zähler (also den Wert der Schleifenvariable). Um das zu erreichen verwenden wir "intern" nach wie vor die Prozentberechnung (um den Anforderungen durch die Fortschrittsanzeige zu entsprechen), doch der angezeigte Text ist dann der Variablenwert.

Der "Mehrwert" des Beispielprogramms:
Haben Sie bemerkt, daß die Schaltfläche start während der Verarbeitung deaktiviert ist? Das ist eine zwar simple, jedoch idiotensichere Methode, um das versehentliche mehrfache Ausführen der Verarbeitung zu verhindern. Stellen Sie sich vor, diese Schaltfläche wäre nicht deaktiviert: Das würde es dem Benutzer ermöglichen, die Verarbeitung wieder und wieder zu starten obwohl sie bereits (bzw. noch) läuft. Fragen Sie mich nicht, was dann passieren würde... Sie können's ja mal selbst ausprobieren. Ich würde es ganz pauschal "unvorhersehbares Verhalten" nennen. Aber wie das Beispielprogramm zeigt, läßt sich das relativ problemlos vermeiden.
Außerdem ist es ein nettes Beispiel für das, was ich einen "guten Stil" der Benutzerführung nenne: Die Oberfläche des Programms repräsentiert nicht nur den momentanen Programmzustand (es gibt dem Benutzer also Informationen zurück) sondern zeigt auch selbsterklärend, daß diese Verarbeitung nicht erneut gestartet werden kann, bevor sie nicht beendet wurde.

Dieses Beispiel soll nur zeigen, welche Möglichkeiten einem durch die Fortschrittsanzeige geboten werden und einen Einblick darin geben, wie man sie verwendet. Beachten Sie auch, daß ich in der Berechnung des Prozentwerts die `trunc()` Funktion verwendet habe, um einen ganzzahligen Wert zu erhalten. `Trunc()` gibt im Prinzip nur den ganzzahligen Teil eines Werts zurück (also das, was "vor dem Komma" steht) - so wie die `int()` Funktion in Basic.
Falls Sie neugierig sind: Ändern Sie doch 'mal probeweise die Größe der Fortschrittsanzeige so, daß deren Höhe größer als die Breite ist - Chris' Steuerelement zeigt den "Prozentbalken" nun automatisch vertikal an! Ist das nicht klasse? Das einzige Problem bei den vertikalen Fortschrittsanzeigen ist, daß unter Umständen nicht mehr genügend Platz für den nach wie vor horizontalen Text darauf zur Verfügung steht.
(Es reicht jedoch in den meisten Fällen zur Anzeige des Prozentwerts.)

Abschließender Hinweis:
Der angezeigte Text auf der Fortschrittsanzeige wird grundsätzlich automatisch zentriert - sowohl horizontal als auch vertikal.

#### 4. Verwenden des Bildanzeige-Steuerelements

Während DrDialog standardmäßig nur in der Lage ist, mit Bitmap-Grafiken (.BMP) umzugehen, ist dieses Steuerelement in der Lage, alle Grafiktypen zu verwenden, die von OS/2 unterstützt werden. Gut, eigentlich sind es vielmehr die Formate, die von MMPM/2 unterstützt werden, aber das soll uns hier erst einmal egal sein. Anfänglich ähnelt die Verwendung dem, was wir von der Fortschrittsanzeige bereits kennen:
Neben dem Laden der Bibliothek benötigt man ebenfalls ein Benutzer-Steuerelement (user control) im gewünschten Dialog, stellt dessen Größe und Position ein und wählt aus dessen Kontextmenü den Eintrag style. Im Eingabefeld class gibt man dann DRD_IMAGE ein, damit das Steuerelement in DrDialog richtig registriert wird und die Kommunikation mit Chris' Bibliothek funktioniert. Nun sollten Sie dem Steuerelement noch einen Namen geben. Da ich aus Gewohnheit immer ein Kürzel für den Steuerelementtyp mitangebe, wäre das bei mir "img"-Irgendwas, beispielweise imgVorschau, wenn das Steuerelement verwendet würde, um eine Vorschau von Bildern zu ermöglichen.

Die in das Element zu ladende Grafikdatei wird mittels der Funktion `text()` angegeben. Hier verwendet man einfach den vollqualifizierten Dateinamen der Grafik (also inklusive Laufwerksbuchstabe und Pfad) und das war's schon. Um beispielsweise die Datei `C:\MMOS2\IMAGES\ARTDECO.JPG` mit der Funktion `text()` zu laden, sähe der Aufruf so aus:

>     call text "C:\MMOS2\IMAGES\ARTDECO.JPG"

Und wenn man das aus einer globalen Prozedur heraus macht, wäre es entsprechend:

>     call imgVorschau.text "C:\MMOS2\IMAGES\ARTDECO.JPG"

Soviel zur grundsätzlichen Verwendung. Nach einigen Tests werden Sie festgestellt haben, daß das Steuerelement die geladenen Bilder automatisch auf seine eigene Größe dehnt oder staucht. Schön - aber in den meisten Fällen ist das nicht unbedingt erwünscht. Wie kann man das ändern?
Es gibt leider keine Möglichkeit eines direkten Funktionsaufrufs an das Steuerelement oder eine entsprechend einstellbare Eigenschaft. Die einzige Möglichkeit wäre hier, die Größe des Steuerelements aus dem Programm heraus zu ändern, um zumindest eine proportionale Darstellung des Bildes (verkleinert oder vergrößert) zu erreichen. Daraus wiederum ergibt sich die Notwendigkeit, die Bildgröße zu kennen - Breite und Höhe. Uff. Wie kommt man nun an diese Informationen? Es gibt drei mir (aktuell) bekannte Möglichkeiten - in Form von eben drei DLLs mit den notwendigen Funktionen:

1. gbm (das generalized bitmap module) von Andy Key
   Enthalten sind Textmodus-Programme ("VIO") mit denen die Bildmaße abgefragt werden und mittels abgefangener Hintergrundausgabe ermittelt werden können. Das funktioniert zwar ziemlich gut, erfordert aber einen ziemlich hohen "Zusatzaufwand". Außerdem müßten im nächsten Schritt die Abmessungen des Steuerelements in Abhängigkeit von den ermittelten Bildmaßen proportional berechnet werden. Nach dem Ändern der Steuerelementgröße könnte dann das eigentliche Bild geladen werden.

2. rexxweb von Igor Pool
   Hierbei handelt es sich um eine mit REXX verwendbare DLL (genau wie die von Chris, die wir gerade besprechen), die eine Vielzahl nützlicher Funktionen enthält. Obwohl in erster Linie für die Verwendung auf OS/2-Webservern entworfen, enthält sie die Funktion `getImageSize()`, die man hervorragend auch überall anders einsetzen kann und die genau das macht, was wir benötigen. Der wirklich interessante Aspekt an dieser Funktion ist, daß man ihr Maximalwerte mitgeben kann. Zurück erhält man dann die proportional berechneten Maße des Bildes. Das würde in unserem Beispiel bedeuten, daß man die Funktion aufruft und ihr neben dem Namen der Grafikdatei auch die Größe des Steuerelements (Breite und Höhe) mit übergibt. Als Rückgabe erhält man dann das Grafikformat der Datei (GIF, BMP und JPG werden unterstützt) zusammen mit den Werten für Breite und Höhe, die proportional so berechnet wurden, daß die größere Seite davon (Breite oder Höhe) genau in das Steuerelement paßt. Was man im Prinzip nun nur noch machen muß, ist die Größe des Steuerelements einfach auf die zurückgegebenen Werte Breite und Höhe zu ändern und die Grafik zu laden - fertig! Tolle Software.

3. XtraRexx von Martin Kiewitz
   Auch eine DLL mit vielen nützlichen Funktionen. Diejenige, die uns im aktuellen Kontext interessiert, heißt `XtraGetPictureInfo()`und gibt ebenfalls eine Zeichenkette zurück, die das Grafikformat des Bildes sowie dessen Breite und Höhe in Pixeln und die Farbtiefe enthält. Das Rückgabeformat entspricht dabei z.B. "JPG 600x480x24". Die unterstützten Bildformate sind PNG, JPG und GIF.
   Beachten Sie aber, daß die Berechnung der "Ziel"größe des Steuerelements unter Einhaltung der Proportionen auch hier wieder in Ihrer eigenen Verantwortung liegt, genau wie bei gbm.

Chris' Bildanzeige unterstützt außerdem das Auswählen eines Bildausschnitts. Ich werde diese Funktion hier nicht im Detail besprechen. Wenn Sie an weiteren Informationen hierzu interessiert sind, schauen Sie einfach 'mal in Chris' readme-Datei. Dort sind ausreichende Erklärungen erhalten. Ich erkläre hier nur kurz, um was es dabei geht:
Zunächst muß man dieses Merkmal aktivieren, indem man ein spezielles Format der `text()` Funktion verwendet. Nachdem der Anwender dann irgendwann einen Bereich ausgewählt hat, kann man die Koordinaten des Bereichs abfragen - wiederum mit der `text()`-Funktion. Im Anschluß kann man nun den ausgewählten Bereich der Grafikdatei in eine neue Grafikdatei zuschneiden, indem man dafür im Hintergrund geeignete parametersteuerbare Hilfsprogramme (wie beispielsweise das oben genannte GBM) verwendet.

#### 5. Verwenden des "Tooltip"-Steuerelements

Tooltips sind überall. Und 'mal ganz ehrlich - sie sind schon ziemlich hilfreich. Warum sie also nicht auch selbst verwenden?
Oder wie Ballmer sagen würde: "Wir brauchen tooltips! Tooltips! Tooltips! ..."

Chris' Implementierung von tooltips bedient sich des Hint-Verfahrens von DrDialog. Für nahezu jedes Steuerelement in DrDialog kann ein `hint`-Text mit bis zu 127 Zeichen angegeben werden. Mittels eines speziellen Aufrufs kann man dann ein bestimmtes Steuerelement zum Standardausgabe-"Bereich" für Hints erklären. Ein Beispiel für die Funktionsweise dieses Verfahrens ist direkt in DrDialogs Entwicklungsumgebung enthalten: Sobald sich der Mauszeiger über einem Steuerelement (oder auch Dialog) befindet, wird in der Statuszeile des DrDialog-Hintergrundfensters ein entsprechender Text ausgegeben. Was Chris' Steuerelement bewirkt, ist daß es

1. der Standardausgabebereich für Hints wird
2. zur Laufzeit ein kleines Fenster mit dem jeweiligen Text an der Mausposition erzeugt
3. die weiteren Eigenschaften der Hilfsanzeige steuert (Dauer, Verzögerung usw.)

Aber wie setzt man den Hilfetext (Hint) für ein Steuerelement?
Indem man das Kontextmenü des fraglichen Steuerelements aufruft und dort den Eintrag "hint" anklickt. ;-)
Dann gibt man den Text ein. Dieser Text erscheint dann später zur Laufzeit in einem kleinen Fensterbereich am Mauszeiger, sobald dieser sich über dem Steuerelement befindet. Seit Version 0.1.3 unterstützt Chris' Bibliothek auch mehrzeilige Texte. Damit dies aber funktioniert, müssen die Zeilenumbrüche mittels eines Steuerzeichens angegeben werden, was - leider - nur per code möglich ist (und somit nicht bereits zur Entwurfszeit). Wie dem auch sei - hier ein Beispiel, wie man es aus dem code heraus macht:

>     call hint "Dies ist ein zwei-" || "0A"x || "zeiliges Hilfefenster."

Da wir nun wissen, wie man den Hint-Text für Steuerelemente angibt, lassen Sie uns schauen, wie man sie als Tooltips angezeigt bekommt:
Und ja: Sie müssen natürlich zunächst die Bibliothek laden, damit das Tooltip-Steuerelement funktioniert (sofern sie die Bibliothek in Ihrem Programm nicht bereits für andere Zwecke schon geladen hatten). Und natürlich müssen Sie auch wieder ein Benutzersteuerelement (user control) auf Ihren Dialog legen und aus dessen Kontextmenü den Eintrag style wählen. Als Klasse (class) geben Sie nun aber DRD_BUBBLEHELP ein. Ach, und wo Sie gerade dabei sind - geben Sie ihm einen netten Namen wie hlpTooltip oder so. Tut mir leid, falls ich Ihnen mit den Namen auf die Nerven falle... ;-)

Als nächstes müssen wir - wie ich bereits bemerkte - das neue Steuerelement zum Standardausgabebereich für Hint-Texte machen.
Es gibt zwei Arten von Hints in DrDialog (Dialog- und Steuerelement-Hints) - somit brauchen wir auch zwei Aufrufe:

>     call IsDefault "D"
>     call IsDefault "C"

Vorzugsweise codieren Sie das in der `INIT-Ereignisroutine` des DRD_BUBBLEHELP Benutzersteuerelements (bzw. "hlpTooltip"). Und das war's.

Nun, zumindest im Prinzip.
Die etwas raffinierteren Funktionsaufrufe beschäftigen sich nun mit den erweiterten Eigenschaften, wie z.B. der Verzögerungszeit, bevor unsere Tooltips erscheinen, oder der Anzeigedauer, bevor sie wieder verschwinden. Die Verzögerung wird in Millisekunden angegeben unter Verwendung der folgenden Syntax:

>     call text "#delay 250"

Damit dauert es eine Viertelsekunde, bevor der Tooltip angezeigt wird, nachdem der Mauszeiger über einem Steuerelement stehen geblieben ist. Die Anzeigedauer wird ebenfalls mittels der `text()`-Funktion und in Millisekunden angegeben, verwendet aber den Parameter `#show`:

>     call text "#show 3000"

Damit bleiben Tooltips 3 Sekunden angezeigt.
Beachten Sie, daß der eigentliche "text" der tooltips von der "hint"-Eigenschaft abgeleitet wird, die für jedes Steuerelement festgelegt werden kann. Standardmäßig ist diese Eigenschaft leer, wodurch also kein Tooltip angezeigt wird. Die übrigen Funktionen dienen zur Einstellung von Farbe und Schriftart, die im Tooltip-Fenster verwendet wird. Die Schriftart wird genau wie in der Fortschrittsanzeige eingestellt:

>     call font "8.Helv"

Zum Beispiel. Oh - da fällt mir ein - die Höhe und Breite des Tooltipfensters wird von Chris' Steuerelement zur Laufzeit dynamisch in Abhängigkeit von der gewählten Schriftgröße und dem Textinhalt bestimmt.

Nun zu den Farben. Auch diese werden wie bei der Fortschrittsanzeige angegeben:
Die Vordergrundfarbe bezieht sich auf den angezeigten Text, die Hintergrundfarbe auf die Farbe des Tooltip-Fensters:

>     call color "-", "#63 63 63"
>     call color "+", "#63 255 63"

Damit erscheinen unsere Tooltips nun als hellgrüner Text auf dunkelgrauem Hintergrund.
Zumindest theoretisch. Aus irgendeinem seltsamen Grund kriege ich nur schwarzen Text hin. Hmm... das muß ich mir noch mal genauer anschauen.

Da alle diese Funktionen global sind (sich also auf sämtliche Tooltips beziehen) könnten wir sie doch auch alle gemeinsam in die `INIT Ereignisroutine` des Tooltip-Steuerelements einbauen, oder? Damit würde die `INIT Ereignisroutine` wie folgt aussehen:

>     call IsDefault "D"
>     call IsDefault "C"
>     call text "#delay 250"
>     call text "#show 3000"
>     call font "8.Helv"
>     call color "-", "#63 63 63"
>     call color "+", "#63 255 63"

Jawoll. Sehr schön.

#### 6. Verwenden des Histogramm-Steuerelements

Um ehrlich zu sein... ich habe keine Ahnung, wofür man das Steuerelement benutzt. Obwohl ich Histogramme aus so ziemlich jedem Grafikprogramm kenne, habe ich keinen blassen Schimmer, um was es dabei geht. Ich vermute allerdings, daß Chris dieses Steuerelement auf besonderen Wunsch realisiert hat - und zwar für das Entwicklerduo, dem wir das großartige TAME/2 (die Frontend-Anwendung von SANE) zu verdanken haben, ohne welches das Betreiben eines Scanners unter OS/2 wahrhaft unausstehlich geblieben wäre. Genau genommen haben wir ebenso (vermutlich) auch in erster Linie diesem Projekt das Bildanzeige-Steuerelement mit seiner Auswahlfunktion zu verdanken. Aber wie dem auch sei: Das ist ein sehr schönes Beispiel dafür, wie die Allgemeinheit von Verbesserungen profitiert, die aus dem Bedarf von einigen wenigen Leuten heraus geschaffen wurden.
Wenn Sie das Histogramm-Steuerelement interessiert, stehen Ihnen alle benötigten Informationen dazu in Chris' Dokumentation zur Verfügung. A propos: Da das Histogramm-Steuerelement aus Entwicklersicht fast genauso verwendet wird wie das Bildanzeige-Steuerelement, verfügen Sie bereits über einen Großteil des benötigten Wissens - den Rest erfahren Sie aus Chris' `readme`-Datei.

#### 7. Verwenden der Vater/Kind-Funktionen für Dialoge

Absolut erstklassige Funktionen! Wenn Sie sich jemals mit mehrfachen Dialogen "herumgeärgert" haben oder am Versuch gescheitert sind, damit bestimmte Effekte wie hierarchische Einstellungsnotizbücher (a la Mozilla) zu realisieren, dann haben Sie bestimmt am eigenen Leib (schmerzlich) die Grenzen des mit DrDialog Machbaren erfahren, stimmt's? Ich muß Sie allerdings enttäuschen: In diesem Artikel werde ich auf die Funktionen noch nicht eingehen. Der Grund dafür ist, daß es zum besseren Verständnis dieser Funktionen (und ihrer Vorteile) unabdingbar ist, sich zunächst mit mehrfachen Dialogen im "reinen" DrDialog selbst auseinanderzusetzen. Haben Sie also ein wenig Geduld; ich werde mich in das Thema einarbeiten, um einen guten Einstieg auszuarbeiten, und melde mich dann zurück.

### Redistribution - Entschuldigung... Re- was, bitte? ;-)

Wenn Sie beabsichtigen, Ihr mit der Bibliothek "erweitertes" Programm anderen Anwendern zur Verfügung zu stellen, sollten Sie unbedingt daran denken, auch die Bibliothek mitzugeben! Zumindest sollten Sie entsprechende Hinweise in Ihrer `readme`, `fileid.diz` oder Hilfedatei machen und vielleicht noch einen URL für das Herunterladen der DLL angeben, falls sie nicht Bestandteil Ihrer "Distribution" ist.
Außerdem sollten Sie - sofern Sie kein Installationsprogramm verwenden - in Ihren Erläuterungen eine kurze Beschreibung geben, wo die DLL abgelegt werden soll: Die potentiellen Anwender können die DLL wahlweise im Installationsverzeichnis Ihres Programm oder (was ich bevorzugen würde) in einem Verzeichnis ablegen, welches im `LIBPATH` enthalten ist.

Ach, und wo Sie gerade dabei sind - wie wäre es mit einer formlosen Danksagung für Chris' Arbeit?

### Und was kommt dann?

Tja... ich habe keine Ahnung. Ich könnte mir gut vorstellen, als nächstes das Thema "mehrfache Dialoge" anzugehen, unter Berücksichtigung der damit verbundenen Tricks und Stolperfallen. Das würde dann auch die Erläuterungen zu den noch ausstehenden Funktionen aus Chris' Bibliothek beinhalten (Steuerung der Vater/Kind-Verhältnisse von Dialogen). Später könnten wir uns entweder mit Igor Pools `rexxweb`-Funktionsbibliothek beschäftigen oder den Faden meines Autorenkollegen Wolfgang Draxler aufnehmen: Datenbankzugriffe für MySQL mithilfe des SourceForge-Pakets `rexxsql` (von Mark Hessling soweit ich mich da richtig erinnere). Aber unabhängig davon, was kommt - ich muß mich zuerst selbst in das jeweilige Thema einarbeiten, bevor ich mich traue, darüber zu schreiben - also Geduld, bitte... ;-)

Daten und Quellen: Chris Wohlgemuth's extensions: http://www.os2world.com/cdwriting/

Artikelverzeichnis
editor@os2voice.org
< Vorherige Seite | Inhaltsverzeichnis | Nächste Seite >
VOICE-Homepage: http://de.os2voice.org