Ein einfacher Weg, um Header in visuelle Klassen einzubinden

Lisa Slater Nicholls

Sie können #INCLUDE-Dateien für die Beschleunigung Ihrer Arbeit benutzen, sowohl zur Entwicklungs- als auch zur Laufzeit. Um sie allerdings mit Klassenbibliotheken und Formularen zu benutzen, bedarf es einiger besonderer technischer Kenntnisse. In diesem Artikel beschreiben wir die Verwendung von #INCLUDE-Dateien mit .VCX- und .SCX-Dateien. Sie werden lernen, wie diese Tabellen Informationen aus Include-Dateien speichern, so daß Sie diese auch direkt editieren können.


In dem Artikel „Dynamische Toolbars in Aktion", in dem die EDC Datenbankerweiterungen besprochen wurden, konnten Sie sehen, daß visuelle Front-Ends einen entscheidenden Vorteil für die Entwicklungstechniken mit sich bringen. Dieser Artikel folgt auch diesem Prinzip und stellt ein Add-In für den Klassenbrowser der Professional-Version von Visual FoxPro vor, mit dessen Hilfe Include-Dateien eingebunden werden können.

Wenn Sie die Professional-Version von Visual FoxPro besitzen, können Sie dieses Add-In installieren, um dem Klassenbrowser die Möglichkeit zu geben, #INCLUDEs für Formulare und Klassenbibliotheken zu editieren. Wenn Sie mit der Standardversion von VFP 3.0 arbeiten, können Sie die gleichen Ideen verwenden, um Ihren eigenen #INCLUDE-Editor zu erstellen.

Was #INCLUDE macht

#INCLUDE ist eine Präprozessoranweisung. Damit ist gemeint, daß #INCLUDE dem VFP-Compiler einige zusätzliche Anweisungen gibt. Wie Sie wissen, werden PRGs oder andere Sammlungen von ASCII-Anweisungen nicht direkt ausgeführt. Erst kompiliert VFP unsere Programmanweisungen und erstellt eine Objektcode-Version. Im Falle von PRGs besteht eine Objektcode-Version aus einer FXP-Datei. Eine APP ist das kompilierte Resultat eines Projekts, das viele Programme und andere Dateien in sich vereinigt.

Die kompilierte Version einer VCX oder SCX ist keine vom Source getrennte Datei. Dieses Charakteristikum enthält einige wichtige Verzweigungen, die Sie in Kürze verstehen werden. Auf jeden Fall existiert die kompilierte Version, und dieser Objektcode wird von VFP ausgeführt, wenn eine Instanz einer Klasse erstellt oder ein Formular ausgeführt wird.

Die #INCLUDE-Anweisung benennt eine Datei, die Konstanten liefert, mit denen der Compiler Ausdrücke ersetzt, die Sie in dieser Datei definiert haben. Dafür wird eine zweite Präprozessoranweisung verwendet: #DEFINE. Die Datei, die mittels #INCLUDE eingebunden wird, wird häufig Headerdatei genannt, da sie Definitionen liefert, die der Rest des Code benötigt. Darum hat die Datei die Erweiterung .H.

#INCLUDEs _ warum und wie

Viele Werte, die Sie in Ihren Programmen verwenden, werden während der Laufzeit dynamisch geändert _ aber eben nicht alle. Sie können Variablen oder Eigenschaften von Objekten definieren, um die veränderlichen Werte zu halten, vielleicht einen Wert aus einer Tabelle. Zum Beispiel könnte die Variable m.name oder die Eigenschaft oPerson.Name den Wert „Lucy" oder „Gracie" enthalten, wenn Ihr Programm zu verschiedenen Datensätzen in einer Adreßtabelle geht.

Wenn bestimmte Werte nicht geändert werden müssen, braucht VFP sie während der Laufzeit nicht zu prüfen. Dadurch werden sowohl Speicher als auch Zeit gespart. Für diese unveränderlichen Werte sollten Sie #DEFINEs einsetzen, um Konstanten zu erstellen, die von VFP während der Kompilierung geprüft und im Objekt-Code gespeichert werden.

#DEFINEs teilen dem Compiler die Informationen mit, die er benötigt, um die von Ihnen benutzten Ausdrücke im Sourcecode zu identifizieren. Auf diesem Weg kann der Compiler diese Ausdrücke auflösen und durch den Wert ersetzen.

Wenn Ihr Programm den Wert Pi() benutzt, wissen Sie natürlich, daß sich dieser Wert nicht ändert. Sie könnten sich höchstens nicht sicher sein, welchen Präzisionsgrad Sie bei Pi benötigen. Sie sollten ein #DEFINE wie dieses benutzen, das hier von einem Code gefolgt wird, in dem ein Ausdruck durch den Wert ersetzt wird:

#DEFINE Pi 3.141
* ...
m.area = Pi * (Circle.radius ^ 2)
m.diameter = Pi * 2 * Circle.radius

Wenn Sie sich später entscheiden, daß Sie mehr als drei Dezimalstellen für Pi benötigen, brauchen Sie lediglich das eine #DEFINE zu ändern, unabhängig davon, wie häufig Sie diesen Wert in Ihrem Code benutzt haben. Zusätzlich haben Sie keine Probleme mit Tippfehlern, wenn Sie den Wert im Code benötigen.

Wenn Sie sich das HOME()-Verzeichnis von VFP ansehen, finden Sie dort eine Headerdatei mit dem Namen FOXPRO.H. Diese Datei enthält viele Konstanten, die Sie benutzen können, um Ihren Code lesbarer und fehlerfrei zu machen.

Zum Beispiel erfordert das zweite Argument der Funktion MESSAGEBOX() einen numerischen Wert, der die Addition von drei Konstanten beinhaltet. Der Wert identifiziert das Icon, das Sie in der Dialogbox anzeigen wollen, die Buttongruppe, die Sie benutzen wollen, und den Button, der als Default markiert ist.

Wenn Sie eine Dialogbox mit einem Stopzeichen, Ja- und Nein-Button mit einem Default auf Nein benötigen, könnten Sie Code schreiben, in dem der Wert hartkodiert ist, oder Sie benutzen #DEFINEs in Ihrem Code, die den Wert für Sie speichern. Statt #INCLUDE zu benutzen, können Sie sich diese Werte auch einfach aus FOXPRO.H „ausleihen". Ein Beispiel:

#INCLUDE C:\VFP\FOXPRO.H
#DEFINE MY_APP_NAME "My Application"
m.return = MESSAGEBOX("My question", ;
MB_ICONSTOP + MB_YESNO + ;
MB_DEFBUTTON", MY_APP_NAME)

Beachten Sie die explizite Pfadangabe im Namen der Headerdatei. Sie müssen dem Compiler die kompletten Informationen übergeben, damit er die Datei finden kann. Sie können den Pfad auch auslassen, um für verschiedene Kompiliervorgänge unterschiedliche .H-Dateien zu benutzen. Sie müssen dann nur sicherstellen, daß die Headerdatei, die Sie für den entsprechenden Kompiliervorgang einsetzen wollen, im aktuellen Verzeichnis vorhanden ist.

Folgen Sie außerdem den VFP-Standards, wenn Sie mit #DEFINE Konstanten definieren. (Lesen Sie dazu in der FoxPro-Hilfe das Thema „Erstellen von Visual FoxPro-Namen".) Sie können diesen erforderlichen Regeln zusätzliche Konventionen hinzufügen (zum Beispiel Großschreibung oder Präfixe), um Ihre #DEFINE-Konstanten von Variablen oder anderen Ausdrücken in Ihrem Code zu unterscheiden.

Der Code weiter oben in diesem Artikel enthält sowohl ein #INCLUDE, um die Headerdatei FOXPRO.H einzuschließen, als auch ein applikationsspezifisches #DEFINE, um die Dialogüberschrift „My Application" verfügbar zu machen. Dieses Beispiel illustriert ein wichtiges Prinzip: Trennen Sie die Definition Ihrer #DEFINEs von ihrer Verwendung. Wie Sie sehen werden, ist diese Unterscheidung für visuelle Klassenbibliotheken wichtig.

Sie können so viele Headerdateien in eine Datei #INCLUDEn, wie sie wollen, einschließlich weiterer Headerdateien, die innerhalb einer Headerdatei #INCLUDEt werden. Sollten Sie doch einmal den Wert einer Konstanten mit einem applikationsspezifischen Wert überschreiben müssen, können Sie sie #UNDEFINEn und im eingebetteten Header neu spezifizieren.

Es stimmt schon, daß enorm große Headerdateien bewirken, daß der Compiler etwas länger arbeiten muß, um den Code abzuarbeiten, so daß Sie sich über die geringe Geschwindigkeit des Compilers wundern. Ab der Version VFP 3.0b wurde allerdings die Geschwindigkeit, mit der der Compiler #DEFINEs behandelt, besonders bei den visuellen Klassen erheblich erhöht.

Behalten Sie im Hinterkopf, daß der Compiler Ausdrücke nur in der Datei in Konstanten umwandelt, in der sie #DEFINEt wurden. #DEFINEs in #INCLUDE-Dateien werden beachtet, auch wenn die #INCLUDE-Dateien verschachtelt sind, während das #DEFINE einer Programmdatei nicht an eine separate Prozedurdatei „vererbt" wird.

Diese Unterscheidung ist der Grund, aus dem wir sowohl #INCLUDE als auch #DEFINE benötigen. Sie hätten wenig Freude daran, wenn Sie in jede Programmdatei, in der die Funktion MESSAGEBOX() aufgerufen wird, die #DEFINEs für den Icon und die Buttons erneut eingeben müßten.

Allerdings gibt es auch einen guten Grund für diese Beschränkung: VFP kompiliert den Objektcode für jede Datei getrennt. Selbst wenn Sie die Anwendung erstellen lassen, hält die PJX-Datei den Objektcode für jede Datei in einem Datensatz, bevor der Code in einer APP-Datei gebunden wird.

Listing A zeigt Ihnen ein praktisches Beispiel für die Benutzung von #INCLUDE mit visuellen Klassen. Obwohl das Listing Ihnen ein anderes #INCLUDE in einem Programm zeigt, wird in dieser Header-Datei der Name der Klassenbibliothek #DEFINEt. Beachten Sie die Verwendung der ALIAS-Klausel im Kommando SET CLASSLIB und die Wiederholung dieses ALIAS im Argument CREATEOBJECT(). In diesem Fall ändere ich den Namen der Klassenbibliothek für jede Applikation, die dieses PRG benutzt, spreche diese unterschiedlichen Klassenbibliotheken aber immer mit einem gemeinsamen ALIAS an.

* MyProg.H
* _whichapp ist entweder #DEFINEd oder nicht #DEFINEd
* in einem vorherigen projektspezifischen #INCLUDE
#IFDEF _whichapp
#IF _whichapp = "Allen"
#DEFINE APP_CLASS_LIB "GRACIE"
#ELIF _whichapp = "Ball"
#DEFINE APP_CLASS_LIB "LUCY"
#ENDIF
#ENDIF
#IFNDEF APP_CLASS_LIB
* bisher nicht definiert, deshalb Standardvers. verwenden
#DEFINE APP_CLASS_LIB "LENNY"
#ENDIF

* MyProg.PRG

#INCLUDE MyPJX.H
#INCLUDE MyProg.H
SET CLASSLIB TO APP_CLASS_LIB ;
ALIAS AppClass ADDITIVE
myvar = CREATEOBJECT( "AppClass.MyClassName"

Listing A: Verwendung einer Include-Datei als Beispiel

Headerdateien im visuellen Design

Eine visuell erzeugte Klasse oder ein Formular kann #DEFINEs genau wie ein PRG nutzen. Zum Beispiel könnten Sie eine MESSAGEBOX() in einer Methode einer Klasse benutzen und dort die gleichen Button- und Icon-Definitionen benötigen, die Sie bereits früher gesehen haben.

Da jede Methode selbst ein Stück Code ist, sollten Sie die #DEFINEs direkt im Methodeneditor benutzen. Das würde allerdings viele wiederholte #DEFINEs für immer wieder die gleichen Konstanten bedeuten _ und diese „Cut-and-Paste-Vererbung" wollen wir in einer objektorientierten Programmierung gerade verhindern.

Statt dessen bietet uns Visual FoxPro eine Methode, mit der wir eine einzige #INCLUDE-Datei für die gesamte Klasse oder das Formular festlegen können. Natürlich kann auch diese #INCLUDE-Datei viele weitere referenzieren, die ihrerseits #INCLUDEs eingebettet haben. Abbildung 1 zeigt den Include-Datei-Dialog, in dem Sie im Formular-Designer die #INCLUDE-Datei festlegen. Sie erreichen diesen Dialog über das Formular-Menü. (Im Klassen-Designer finden Sie eine entsprechende Dialogbox.)

Nachdem Sie eine Header-Datei festgelegt haben, müssen Sie sicherstellen, daß Sie, wenn Sie das Formular oder die Klassenbibliothek an einen anderen Platz verschieben, diese Datei ebenfalls dorthin kopieren. Andernfalls werden der Klassen- bzw. der Formular-Designer einige Zeit zum Kompilieren benötigen, wenn Sie diese Objekte erneut editieren.

Die Designer erstellen den Objektcode automatisch, sobald Sie Ihre Arbeit speichern und das Fenster des Designers schließen. Sie finden auch keine separate kompilierte Datei auf Ihrer Platte, wie VFP sie schreibt, wenn Sie eine PRG-Datei in ein FXP kompilieren. Wo steht der Objektcode nun aber? Er steht in der VCX oder der SCX in einem Memofeld für jedes Objekt.

Denken Sie über dieses Verhalten von Visual FoxPro einen Moment lang nach. Dieses Verhalten von VFP ist sowohl Fluch wie auch Segen für die Entwicklungsarbeit. Positiv ist, daß Sie Ihre Konzepte schnell testen können. Sie können ein CREATEOBJECT() oder ein DO FORM ausführen, ohne eine APP zu BUILDen oder andere separate Schritte für die Kompilierung auszuführen.

Abbildung 1

Auf der Negativseite ist zu vermerken, daß die Ableitung einer Klasse einen neuen Datensatz mit seiner eigenen Include-Datei und seinem eigenen Objektcode erfordert. Die abgeleitete Klasse kann sich nicht darauf verlassen, daß sie immer die #DEFINEs der Klasse, von der sie abgeleitet ist, zur Verfügung hat _ solange Sie nicht die gleiche Include-Datei für den neuen Datensatz spezifiziert haben.

Es gibt zwei grundsätzliche Lösungsansätze für dieses Problem. Die erste Strategie besteht darin, daß Sie eine oder mehrere Header-Dateien in der Header-Datei der abgeleiteten Klasse #INCLUDEn. Ihre erste Header-Datei der Klasse enthält lediglich globale Konstanten. Wenn Sie sich nun in der Klassenhierarchie abwärts bewegen, betten Sie ein #INCLUDE für die Header-Datei jeder Elternklasse ein, die zusätzliche #DEFINEs enthält, die früher nicht benötigt wurden.

In der zweiten Strategie #INCLUDEn Sie nur eine kleine klassenspezifische Header-Datei in jede Klasse, die #DEFINEs benutzt. Da der Compiler den Objektcode für jeden Level in seinem spezifischen Datensatz speichert, benötigt der Compiler lediglich den Zugriff auf die Konstanten, die im spezifischen Level benötigt werden, um den Objektcode der gegebenen Klasse zu erstellen. Der Code, der mit einer abgeleiteten Klasse kompiliert und gespeichert wurde, braucht wahrscheinlich nicht jede globale Konstante. Dies gilt vor allem dann, wenn Ihre Klasse das Verhalten der Klassen, von dem sie abgeleitet wurde, erweitert oder überschreibt. Dementsprechend ist das Kopieren der #DEFINEs nicht notwendig.

Auswertung des Klassenkatalogs

Die Eigenschaften und Methoden des Klassenkatalogs sind in der FoxPro-Hilfe gut dokumentiert. Wenn Sie den Katalog ausführen, enthält die globale Variable _oBrowser eine Referenz, mit deren Hilfe Sie auf die eingebetteten Objekte interaktiv zugreifen können. Zum Beispiel ruft die Zeile

_oBrowser.RefreshClassList()

die Methode RefreshClassList() des Klassenkatalogs auf.

Das Hauptprogramm jedes Add-Ins muß entweder mit dem Befehl PARAMETERS oder besser noch mit LPARAMETERS beginnen, um das Objekttyp-Argument des Klassenkatalogs (eine Referenz auf sich selbst) zu erhalten. Mein Add-In-Button benutzt dafür seine Eigenschaft THIS.oBrowser statt _oBrowser, in der er die übergebene Referenz speichert. Diese Prozedur ist nicht nur sicherer, sondern auch exakter, wenn Sie mehrere Instanzen des Klassenkatalogs ausführen, die jede ihre eigenen eingebetteten Add-Ins enthält.

Beachten Sie, daß die Dokumentation viele Eigenschaften des Klassenkatalogs als „sollte nur gelesen werden" auflistet. Damit ist aber nicht „für Ihre Information, Benutzung verboten" gemeint, sondern einfach, daß Sie diese Eigenschaften nicht verändern sollten, um Fehler des Klassenkatalogs zu vermeiden. In der Prozedur Recompile() des #Include-Editors schließe ich die Datei, die in der Eigenschaft cFilename des Klassenkatalogs referenziert ist, und öffne Sie erneut. Das ist aber nur sicher, weil ich diese Aktionen in einer modalen Dialogbox ausführe, bevor ich zum Klassenkatalog zurückkehre.

Dafür öffne ich diese Tabelle im gleichen Arbeitsbereich und stelle den Datensatzzeiger auf exakt die Position, auf der ich ihn vorgefunden habe. Mein Dialog arbeitet mit der aktuellen Datensitzung, teilt sich die Datensitzung also mit den Klassenkatalog, da sich der Code, der die Dialogbox aufruft, in einem Objekt des Klassenkatalogs befindet.

Der Klassenkatalog führt eine ganze Anzahl Tricks aus, von denen viele nicht dokumentiert sind, so die meisten Überwachungen der Mausbewegungen. Er führt ständig einen Refresh auf sich selbst und die Positionen seiner Datensatzzeiger aus. In Ihrem eigenen Code können Sie sich darauf verlassen, daß diese Satzzeiger nicht bewegt werden, bis Sie es selbst tun. Da der Klassenkatalog die Zeiger aber ständig verschiebt, sollte die erste Aktion Ihres Codes sein, ihn auf die Stelle zu verschieben, an der Sie ihn erwarten, abhängig vom Status des Klassenkatalog-Dialogs in dem Moment, in dem er die Kontrolle abgibt. Ich benutze folgenden Code im Init() meiner Dialogbox:

THIS.oInclude.oBrowser.SeekClass(THIS.oInclude.oBrowser.oClass)

Dieser Code stellt sicher, daß der Datensatzzeiger der VCX- oder SCX-Datei dem aktuell markierten Punkt der Ausgabeliste des Klassenkatalogs entspricht. Ich benötige diese Information, um in dem Fall, daß das Feld Reserved8 gefüllt ist, den Namen einer Include-Datei zu erhalten.

Nachdem Sie nun über den Klassenkatalog verfügen können, haben Sie Zugriff auf viele seiner Methoden, um Ihre Erweiterungen zu implementieren. Warum wollen Sie zum Beispiel Ihre eigene Methode TrimPath() schreiben, wenn Sie über diese mit einer einzelnen Codezeile verfügen können: THIS.oBrowser.TrimPath(<Dateiname>)? Das Wiederverwenden von Code ist es doch schließlich, was die objektorientierte Entwicklung ausmacht.

Intelligente #INCLUDEs

Ein Teil Ihrer Strategie ist vorgegeben: Applikationsspezifische Werte (wie ich sie für den Titel der MESSAGEBOX() eingesetzt habe) werden nie in der Standard-Klassenbibliothek #DEFINEt. Da VFP diese Information direkt in die VCX kompiliert, erscheint sie in jedem Projekt, das die VCX-Datei benutzt.

Nehmen wir einmal an, die MESSAGEBOX() wäre eine AboutBox()-Klasse meiner Bibliothek statt einer eingebauten VFP-Funktion. Wenn ich nun vorhätte, MY_APP_NAME in einer Bibliothek, die in mehreren Applikationen benutzt wird, zu #INCLUDEn oder zu #DEFINEn, müßte ich jedesmal, wenn ich eine APP erstelle, die Option „Alle Dateien nochmals kompilieren" des Projektmanagers benutzen. Andernfalls würden alle Applikationen den Namen anzeigen, den ich #DEFINEt hatte, als VFP das letzte Mal die VCX, welche die Dialogbox enthält, kompilierte.

Gehen wir weg von der projektspezifischen Klassenbibliothek in Listing A. Mein Hauptprogramm enthält lediglich einen Verweis auf eine klassenspezifische Bibliothek, da der kompilierte Code einer PRG-Datei (anders als der von VCXen) durch das Projekt separat gespeichert wird. Alles, was ich brauche, ist eine im Programm #INCLUDEte Header-Datei mit den #DEFINEs der applikationsspezifischen Werte, die sich im gleichen Verzeichnis wie das dazugehörige Projekt befindet.

Schnelle #INCLUDEs

Häufig ist es recht ermüdend, die gleichen Include-Dateiinformationen jeder Klasse und jeder neu abgeleiteten Klasse einzeln mitzugeben. Selbst wenn Sie für jede Klasse eine andere Header-Datei benutzen, müssen Sie die Informationen über fertiggestellte Klassen ändern, wenn Sie Ihre Entwicklungsstrategie ändern. Sie könnten sogar Probleme haben, das Designfenster nur zu öffnen, wenn Sie eine VCX- oder SCX-Datei verschoben haben und VFP die Include-Datei nicht finden kann.

Sie sollten sich unbedingt klarmachen, daß die Include-Dateiinformationen in lesbarer Form in den SCX- und VCX-Tabellen gespeichert ist. VFP speichert die Namen der Include-Dateien im Memofeld „Reserved8" des Hauptdatensatzes jeder Klasse einer VCX-Datei. (Sie finden zusätzliche Datensätze für jede Klasse, verbunden durch den gleichen Wert im Feld UniqueID.) In einer SCX-Datei ist das Formular bzw. der Formularsatz im Datensatz SCREEN gespeichert.

Sie können die VCX- oder SCX-Dateien mit einem USE öffnen und die darin gespeicherten Werte REPLACEn, einen BROWSE über die Datei laufen lassen oder jeden anderen programmatischen Mechanismus anwenden, den Sie auch bei Tabellen benutzen. Zusätzlich können Sie den Wert des Feldes „Objcode" auf "" setzen, um die Klassen beim nächsten Compilerlauf neu zu kompilieren oder, wenn Sie Probleme haben, die Klassen im Designer zu öffnen, nachdem Sie die VCX bzw. SCX-Tabellen verschoben oder manipuliert haben. Abbildung 2 zeigt Ihnen eine geöffnete VCX-Datei mit mehreren geöffneten Memofenstern, die auch den Objektcode und das Feld „Reserved8" anzeigen.

Abbildung 2

Automatisierte #INCLUDEs

Sie haben gesehen, wie Sie in den Systemtabellen suchen können, wenn es nötig ist. Lassen Sie uns Ihrer Applikation nun ein visuelles Front-End hinzufügen, um diese mächtigen Funktionen angenehmer zu gestalten.

Der Sourcecode zu diesem Artikel, den Sie auf der Begleitdiskette finden, besteht aus einer Anzahl Dateien mit dem Namen ADDINCL.*, der ein „Include-Add-In" für den Klassenkatalog von VFP bereitstellt. Kopieren Sie sich alle diese Dateien in ein einzelnes Verzeichnis auf Ihrer Festplatte. Anschließend starten Sie den Klassenkatalog aus dem Extras-Menü oder geben Sie im Befehl-Fenster DO (_BROWSER) ein.

Nun müssen Sie das Add-In wie folgt im Katalog „registrieren":

_oBrowser.AddIn("Includes", ;
"<fullpath>\ADDINCL.PRG", "iNIT")

_oBrowser ist die Objektreferenz für den aktiven Klassenkatalog. Der Aufruf dieser Methode weist den Katalog an, das neue Add-In anzuzeigen. Der zweite Parameter legt das Programm fest, das als dieses Add-In ausgeführt werden soll; stellen Sie also sicher, daß Sie hier den richtigen Pfad angeben. Durch den dritten Parameter erfährt der Klassenkatalog, daß er dieses Add-In automatisch aus einer seiner Methoden heraus ausführen soll; nachdem es im Add-In-Menü registriert ist, können Sie es mit dem Standard Add-In-Button übergeben.

Da das Init() des Katalogs die passende Methode ist, müssen Sie nun den Klassenkatalog schließen und erneut starten, um das Resultat zu sehen. (Das Add-In ist nun permanent im Klassenkatalog registriert, solange Sie es nicht löschen.) Sie sollten nun wie in Abbildung 3 den neuen #-Button direkt unter der Typ-Combobox sehen.

Klicken Sie auf den #-Button, um den #Include-Editor aufzurufen. Die Dialogbox, die nun erscheint, wird sich leicht von Abbildung 3 unterscheiden, je nachdem, was Sie aktuell im Klassenkatalog ausgewählt haben. Die Abbildung zeigt, daß eine einzelne Klasse ausgewählt ist. Wenn Sie stattdessen eine VCX-Datei auswählen, wird die Option „bearbeiten" ausgewählt und deaktiviert, da der Dialog mit den entsprechenden Filtern mit der gesamten Datei arbeitet. Wenn Sie mit einer SCX-Datei arbeiten, wird diese Option nicht ausgewählt und deaktiviert, da eine SCX-Datei lediglich eine Header-Datei besitzt, mit der sie arbeitet.

Dementsprechend reflektiert die „Nur Formulare"-Checkbox in der Abbildung, welchen Filter Sie im Klassenkatalog gesetzt haben. Die Option wird deaktiviert, wenn Sie keinen Filter gesetzt haben.

Die Dialogbox hat zwei Hauptaufgaben: Editieren der Reserved8-Werte in den SCX- oder VCX-Dateien entsprechend des Filter-Ausdrucks und optional das erneute Kompilieren des Objektcodes in diesen Dateien, nachdem Sie die Datensätze editiert haben. Die erste Aufgabe benutzt die REPLACE-Syntax mit einen Makro-Ausdruck, der den aktuellen Filter liefert. Sie finden den Code in der Destroy()-Methode des Formulars.

Wenn die Destroy()-Methode mit Hilfe der Systemvariablen _TALLY feststellt, daß Sie Datensätze geändert haben, und wenn Sie die Option „Nach Bearbeitung kompilieren" aktiviert haben, ruft Destroy() die Methode Recompile() auf. Recompile() benutzt das neue FoxPro-Kommando:

COMPILE FORM (THIS.oInclude.oBrowser.cFilename) ALL

Abbildung 3

Sie können dieses Kommando jederzeit benutzen, wenn Sie ein erneutes Kompilieren außerhalb der BUILD-Operation des Projekts durchführen wollen. Beachten Sie, daß dieses Kommando sowohl für Formulare als auch für Klassen gültig ist; es gibt kein Kommando COMPILE CLASS (Hinweis: Nicht zu verwechseln mit dem sehr wohl vorhandenen Befehl „COMPILE CLASSLIB").

Das Schlüsselwort ALL erweckt den Eindruck, als wäre „alle Klassen" gemeint; tatsächlich wirkt das Kommando COMPILE FORM immer auf alle Objekte in der speziellen Tabelle. Das Schlüsselwort ALL bezieht sich auf die Komplilierung über mehrere Plattformen. Sie könnten eine andere Checkbox als ich bevorzugen, um dieses Feature auszuführen.

Sie werden feststellen, daß ich den Namen der Datei, die ich kompilieren möchte, indirekt referenziert habe. Das Add-In-Objekt, also der #-Button, erhält eine Referenz auf den Klassenkatalog als Parameter, wenn er vom Klassenkatalog instantiiert wird. Das Add-In-Objekt kann mit Hilfe dieser Referenz auf die Eigenschaften und Methoden des Klassenkatalogs zugreifen.

Wenn der Button das Formular für den Editor-Dialog ausführt, benutzt er die Syntax DO FORM <name> WITH THIS, indem er dem Formular eine Referenz auf sich selbst übergibt. Nun können die Methoden des Editor-Formulars Daten mit dem Klassenkatalog austauschen, indem sie sich den Referenzbaum hocharbeiten. Dabei stellt THIS.oInclude.oBrowser.cFilename den Wert zur Verfügung, der in der Eigenschaft cFilename des Klassenkatalogs enthalten ist. Diese Eigenschaft enthält den vollen Pfad und den Namen der VCX oder SCX, die entweder den aktuell gewählten Eintrag der Liste des Klassenkatalogs enthält oder die selbst der Eintrag ist. Lesen Sie den Kasten „Auswertung des Klassenkatalogs" weiter unten, um mehr Informationen zu erhalten, wie Sie sowohl von innerhalb eines Add-In als auch interaktiv auf die Attribute des Klassenkatalogs zugreifen können.

Nun sind Sie dran

Wenn Sie nicht mit dem Klassenkatalog arbeiten möchten, könnten Sie auf einer Toolbar, die der lsnToolbar aus dem Artikel „Dynamische Toolbars in Aktion" entspricht oder von ihr abgeleitet wurde, einen #INCLUDE-Button einsetzen. Anstatt den Klassenkatalog für das Öffnen und Anzeigen der Klassen, die sich in VCX- oder SCX-Tabellen befinden, zu benutzen, können Sie eine Listbox aufbauen, die alle benötigten Indizes und Filteroptionen enthält, die Sie benötigen. Editieren Sie die Reserved8-Felder mit allen Einschränkungen, die Ihnen sinnvoll erscheinen. Benutzen Sie meine Dialogbox als Anregung.

Sie werden den Prozeß des Editierens schnell erreichen, aber Sie sollten mit Kopien Ihrer Formulare und Klassenbibliotheken arbeiten, bis Sie mit den Mechanismen, die hier ablaufen, vertraut sind. Seien Sie auch vorsichtig mit dem expliziten Kompilieren, und CLEARen Sie alle CLASSen, die sich im Cache befinden könnten, bevor Sie eine neue Instanz einer Klasse, die Sie verändert haben, erstellen.

#INCLUDEs und #DEFINEs bieten wichtige Erweiterungen für visuell und von Hand erstellte Dateien an. Sie könnten sie zum Beispiel für die Lokalisierung von Anwendungen benutzen, wenn Sie unterschiedliche Ausgabestrings für unterschiedliche Compilerläufe definieren. Sie sind auch sinnvoll, wenn Sie Assertions (Tests, die im Code Ihrer endgültigen Anwendung nicht enthalten sein sollen) oder andere Debugging-Prozeduren einsetzen wollen.

Lisa Slater Nicholls ist von Neuseeland aus international in den Bereichen Training und Beratung tätig. Ihr Schwerpunkt ist der Review von Programmcode, Performance und Benutzeroberflächen von in Entwicklung befindlichen Applikationen unter Visual FoxPro. Sie können Sie per eMail unter lisa@softspoken.co.nz erreichen.