Erstellen von Menüs in Visual FoxPro Rodney Hill Wenn Sie Windows95-kompatible Anwendungen entwickeln, wollen Sie diesen auch ein Menüsystem zuordnen, das dem Anwender die Funktionalitäten der Anwendung zur Verfügung stellt. Das Entwicklerhandbuch von Visual FoxPro behandelt die Mechanismen, mit denen Sie das Menüsystem erstellen und ist daher ein guter Startpunkt für die Entwicklung. Dieser Artikel fügt noch Informationen für folgende Bereiche hinzu: Ihren Anwendungen Menüs hinzufügen Den Menüs, die mit dem Menüdesigner erstellt wurden, dynamische Elemente hinzufügen Top-Level-Formularen Menüs zuordnen Erstellen von Menüklassen Datengesteuerte Menüs erstellen Die meisten Informationen treffen sowohl auf Visual FoxPro 3.0 als auch auf die Version 5.0 zu. Wenn sich Informationen nur auf VFP 5.0 beziehen, wird das jeweils explizit angemerkt. Ihrer Anwendung Menüs hinzufügen Der schnellste und einfachste Weg für die Erstellung eines Menüs führt über den Menü-Designer. Ich nehme in diesem Artikel daher auch an, daß Sie diesen Weg wählen. Der beste Ansatz bei dieser Arbeit ist wahrscheinlich der, den Microsoft auch beim Internet nimmt: „zu eigen machen und erweitern“. Auf den Menü-Designer bezogen heißt das, daß Sie die Werkzeuge, die uns Visual FoxPro zur Verfügung stellt, benutzen und deren Fähigkeiten durch das Hinzufügen eigenen Codes erweitern. Das Entwickeln von Menüs mit dem Menü-Designer ist im Entwicklerhandbuch von VFP beschrieben. Grundsätzlich läuft der Prozeß folgendermaßen ab: 1. Sie erstellen mit Hilfe des Menü-Designers die Struktur des Menüs und ordnen den einzelnen Menüeinträgen die Befehle zu, die ausgeführt werden, wenn der Anwender den Eintrag auswählt. 2. Sie generieren das Visual FoxPro-Menü (eine Programmdatei mit der Erweiterung .MPR). 3. Sie führen das Programm in Ihrer Anwendung aus, um das Menü zu erstellen. Wird ein Menüprogramm ausgeführt, macht es nichts anderes als jedes andere VFP-Programm auch: Es führt seine Aufgabe aus (in diesem Fall die Definition und die Anzeige des Menüs) und beendet sich anschließend. Das Menüprogramm bleibt nicht im Hintergrund aktiv, um auf eine Menüauswahl des Anwenders zu warten. Die Befehle, die nach der Auswahl eines Menüpunkts ausgeführt werden, sind systemweit verfügbar. Das bedeutet, daß sie Zugriff auf alle globalen Variablen (und globale Objektreferenzen) sowie auf alle die Prozeduren/Funktionen haben, deren Programmdateien mit dem Befehl SET PROCEDURE TO... bekanntgemacht wurden. Da Sie außerhalb des Codes einer Methode die Befehle THIS oder THISFORM nicht einsetzen können, dürfen Sie diese auch nicht im Code, der nach einer Menüauswahl ausgeführt wird, benutzen. Um aus einem Menü heraus Methoden aufzurufen oder Eigenschaften eines Formulars zu verändern, verwenden Sie die Objektreferenz des Objekts, oder allgemeiner _SCREEN.ActiveForm. Im Eintrag „Beenden“ des Datei-Menüs könnten Sie beispielsweise die folgende Codezeile verwenden: =_SCREEN.ActiveForm.Release() Erstellen modularer Menüs Sie können im Menü-Designer Ihre gesamte Menüstruktur Ihrer Anwendung erstellen und daraus ein einziges Menü generieren. Allerdings ist das nicht das optimale Vorgehen. Auch im Menü können Sie von den gleichen Vorzügen profitieren, die Sie auch bei der modularen oder objektorientierten Programmierung genießen, indem Sie modulare Menüs entwerfen. Statt für Ihre gesamte Anwendung ein Gesamtmenü zu aufzubauen, erstellen Sie separate Programme für jedes Menü oder für einzelne Menügruppen Ihres Systems. Wenn Sie beispielsweise getrennte Menüs für Datei, Bearbeiten, Fenster, Hilfe und Ihre anwendungsspezifischen Einträge erstellen, können Sie diese in allen Ihren Anwendungen einsetzen; der Code steht aber an einer zentralen Stelle. Bei Bedarf können Sie spezielle Menüs hinzufügen oder auch löschen. Die Erstellung einzelner Menüs im Menü-Designer ist ein einfacher und gerader Weg, modulare Menüs zu erzeugen. Es gibt aber auch andere Werkzeuge und Techniken für diese Aufgabe. So hat beispielsweise Andrew Ross MacNeill das Public Domain-Werkzeug GenMenuX geschrieben, mit dem Sie wiederverwendbare Vorlagen für modulare Menüs erstellen können. Sie können sich GenMenuX von verschiedenen Internetseiten sowie aus dem FOXUSER-Forum und natürlich auch aus dem dFPUG-Forum in Compuserve herunterladen. Speichern und Wiederherstellen des Original-Menüs Bedenken Sie in Ihrer Anwendung, daß Sie das bestehende Menü sichern, bevor Sie Ihr eigenes Menüprogramm ausführen und es, nachdem Sie Ihre Anwendung beendet haben, auch wiederherstellen. Als Beispiel ein Auszug aus dem Hauptprogramm einer Anwendung: * Push the existing menu on the menu stack PUSH MENU _MSYSMENU * Display interface DO File.MPR DO Edit.MPR DO MyApp.MPR DO Window.MPR DO Help.MPR DO FORM InitialForm * Allow interface to process events READ EVENTS * Restore the original menu POP MENU _MSYSMENU Der Menü-Stack arbeitet wie alle Stacks nach dem Prinzip last in, first out. Wenn Sie Menü A auf den Stack packen, Menü B ausführen, anschließend Menü B dem Stack hinzufügen, um Menü C auszuführen und anschließend den Befehl POP MENU aufrufen, wird Menü B wiederhergestellt. Ein weiterer Aufruf von POP MENU aktiviert dann Menü A. PUSH MENU _MSYSMENU belegt circa 12 Kilobyte Hauptspeicher, ein vollständiges benutzerdefiniertes Menü eventuell sogar noch erheblich mehr. Sie sollten möglichst für jedes Menü, das Sie auf den Stack packen, ein anderes Menü daraus entfernen, um den Speicherbedarf so niedrig wie möglich zu halten. Die Verwendung von Untermenüs Untermenüs sind Menüs, die geöffnet werden, wenn der Anwender einen Eintrag in einem anderen Menü auswählt. Sie können die Untermenüs zwar auch mit dem Menü-Designer einfach erstellen; es muß aber nicht unbedingt sein. Untermenüs erfordern mehr Mausaktionen durch den Anwender und zeigen nicht die Zusammenhänge und visuelle Benutzerführung wie es eine Dialogbox kann. Anwender akzeptieren selten ein Untermenü mit nur einem Eintrag. Diesen Eintrag könnte der Entwickler auch einfach dem Originalmenü hinzufügen. Wenn Untermenüs unvermeidbar sein sollten, dann sollten Sie sich bemühen, mit einem Shortcut ein Untermenü öffnen zu lassen. Auf keinen Fall sollte man die Menüs zu tief verschachteln. Dynamische Elemente in Menüs, die mit dem Menü-Designer erstellt wurden, integrieren Mit den Menü-Befehlen und -Funktionen von VFP können Sie zusammen mit dem Menü- und dem FormularDesigner dynamische Menüs erstellen, die zur Laufzeit konfigurierbar sind. Die Elemente des Menüs Der Menü-Designer generiert (mit _GENMENU) die Menüdefinition. Wenn Sie diesen Menücode verstehen, sind Sie auch in der Lage, die Menüs, die Sie mit dem Menü-Designer erstellt haben, zu erweitern. Die Terminologie ist bei den Menüs grundsätzlich, und besonders bei FoxPro, immer ein Problem. Die Dokumentation von Microsoft benutzt beispielsweise die Begriffe Menübefehl, Menüoption und Menüpunkt synonym, wenn es um die programmatische Erstellung eines Menüeintrags mit dem Befehl DEFINE BAR geht. Im Code von Visual FoxPro besteht ein Menü aus PADs in _MSYSMENU, es werden POPUPs aktiviert, wenn ein Anwender ein PAD auswählt und die BARs sind die Einträge, die der Anwender wählt, um eine Aktion ausführen zu lassen. Wenn sich dieser Artikel (und auch die VFP-Dokumentation) auf Menüs bezieht, sind ein PAD, ein POPUP oder ein oder mehrere BARs gemeint. Die folgende Abbildung zeigt, wie VFPs Menüdefinition und die Menüelemente zusammengehören. Suchen Sie einmal in der Hilfe von Visual FoxPro nach „Menüs und Menüeinträge“. Sie erhalten eine Liste aller Menüdefinitionen und Befehle, die sich auf die Menüs beziehen, zusammen mit Querverweisen auf die exakte Syntax und auf Beispiele. Das folgende Beispiel betrachtet die wichtigsten Menübefehle in der Reihenfolge, in der sie normalerweise ausgeführt werden. Bevor Sie das Menüsystem Ihrer Anwendung aufbauen, müssen Sie das Systemmenü von Visual FoxPro löschen. Die folgende Codezeile löscht alle Einträge aus dem Systemmenü. SET SYSMENU TO Jetzt können Sie dem Systemmenü einen neuen Eintrag hinzufügen, indem Sie den Befehl DEFINE PAD benutzen. DEFINE PAD padReports OF _MSYSMENU ; PROMPT “\<Berichte” ; MESSAGE “Welcher Bericht soll ausgeführt werden?” Wenn nun ein Anwender auf den neuen Eintrag klickt, passiert nichts. Sie müssen ein Popup mit Bars darauf definieren, um ein Menü anzuzeigen, wie der Anwender es erwartet, wenn er auf ein Menü klickt. DEFINE POPUP popReports MARGIN ON PAD padReports OF _MSYSMENU ACTIVATE POPUP popReports DEFINE BAR 1 OF popReports ; PROMPT “Rechnung” MESSAGE “Drucken einer Rechnung” Wenn der Anwender jetzt auf den Eintrag klickt, wird ihm der Eintrag „Rechnungen“ angezeigt. Das nutzt aber auch noch nicht viel; Sie müssen dem Eintrag erst noch Code hinzufügen, der angibt, welche Aktion mit dem Menüpunkt verknüpft werden soll. ON SELECTION BAR 1 OF popReports REPORT FORM INVOICE.FRX PREVIEW Benutzen der Systemmenüs von Visual FoxPro Zusätzlich zu den selbstentwickelten Menüs können Sie auch vordefinierte Menüpunkte von Visual FoxPro in Ihrer Anwendung einsetzen. Es gibt auch einige gute Gründe, weshalb Sie das tun sollten. Bei den Systemmenüs ist die Funktionalität bereits eingebaut. Das Menü „Bearbeiten“ (Padname MSMEDIT, Popupname _MEDIT) beispielsweise ermöglicht das Ausschneiden, Kopieren, Einfügen und Suchen von Text. Wenn Sie Ihrem Anwender dieses Menü nicht zur Verfügung stellen, kann er diese Aktionen nicht ausführen. Das Menü „Fenster“ (Padname MSMWINDO, Popupname _MWINDOW) zeigt die Namen der aktuell ausgeführten Formulare an und ermöglicht es dem Anwender, diese Formulare zu aktivieren. Unter dem Menüpunkt Ansicht – Allgemeine Optionen des Menüdesigners können Sie eine Position vor oder nach einem Systemmenü-Pad wählen. Um sich alle Systemmenüs und Menüeinträge anzeigen zu lassen, suchen Sie in der Hilfe von VFP nach „Systemmenünamen“ oder benutzen Sie SYS(2013). Die Option „Standardmenü“ fügt Ihrem Menü alle Systemmenüs hinzu. Anschließend können Sie Ihr Menü anpassen. Wenn Sie in VFP 5.0 eine generische Menükomponente einfügen wollen, können Sie im Menü-Designer „Leiste einfügen...“ wählen und anschließend eine einzelne Menüoption aus der Liste auswählen. Falls Sie ein anwendungsspezifisches Menü entwickeln, möchten Sie eventuell die Systemmenüs von Visual FoxPro nicht einsetzen. Sie müssen aber trotzdem die Plazierung des Menüs im Dialog „Allgemeine Optionen“ angeben. Wenn Sie Ihr anwendungsspezifisches Menü links vom Fenster-Menü anzeigen wollen, wählen Sie zunächst in den Allgemeinen Optionen „Vor“ und anschließend in der nun erscheinenden Combo-Box „Fenster“ aus. Sie können auf die Funktionalität der VFP-Systemmenüs programmatisch mit der Funktion SYS(1500) zugreifen. Wenn beispielsweise das aktive Fenster ein Eingabeformular ist, öffnet die folgende Codezeile den „Suchen“-Dialog von Visual FoxPro: ?SYS(1500, "_MED_FIND", "_MEDIT") Deaktivieren einzelner Menüpunkte Einer der häufigsten Gründe, ein Menü der aktuellen Arbeitsumgebung des Anwenders anzupassen, ist die Aktivierung und Deaktivierung einzelner Menüpunkte. Wenn Sie einen Menüeintrag definieren, können Sie die SKIP-Klausel benutzen, um festzulegen, wann ein Menüpunkt deaktiviert werden soll. Falls der Ausdruck feststellt, daß der Befehl SKIP FOR .T. zurückgibt, wird der Menüeintrag deaktiviert. Wenn beispielsweise das Menü „Datei“ den Eintrag „Schließen“ zum Beenden der aktiven Maske enthält, stellt der folgende Code sicher, daß der Eintrag nur dann aktiviert wird und damit genutz werden kann, wenn auch ein Formular aktiv ist: DEFINE BAR 3 OF _MFILE ; PROMPT “Close” ; MESSAGE “Close the currently active form” ; SKIP FOR TYPE("_SCREEN.ActiveForm") != "O" ON SELECTION BAR 3 OF _MFILE _SCREEN.ActiveForm.Release In der Beispielsanwendung Solutions, die mit Visual FoxPro ausgeliefert wird, finden Sie zwei Beispiele, die das Deaktivieren von Menüeinträgen demonstrieren: „Koordinieren von Menüleisten und Symbolleistenschaltflächen“ und „Entfernen/Anzeigen von Häkchen neben Menübefehl“. Wenn Sie diese Beispiele ausführen, können Sie sich mit der F1-Taste anzeigen lassen, wie diese Beispiele implementiert wurden. Die Systemmenüs von VFP stellen automatisch sicher, daß die jeweils passenden Punkte deaktiviert werden. So werden beispielsweise die Einträge „Ausschneiden“, „Kopieren“ und „Einfügen“ des Menüs „Bearbeiten“ (Padname MSMEDIT, Popupname _MEDIT) automatisch deaktiviert, wenn kein Text markiert ist bzw. sich kein Text in der Zwischenablage befindet. Wenn Sie einen Menüeintrag erstellen, können Sie die SKIP FOR-Klausel im Menü-Designer eingeben. Da Sie sowohl die Klausel SKIP FOR als auch den Befehl SET SKIP OF einsetzen können, um ein Menüpopup oder das gesamte Menü durch die Deaktivierung des Eintrags in der Menüleiste zu deaktivieren, handelt es sich hier nicht um eine Standardschnittstelle für Windows-Anwendungen. Ist ein Menü nicht auf eine gegebene Umgebung anwendbar, so muß es insgesamt gelöscht werden. Dies wird im folgenden Abschnitt beschrieben. Menüeinträge mit Formularen verbinden Häufig wollen Sie die Funktionalität eines Formulars erweitern, indem Sie selten genutzte Funktionalitäten im Menü bereitstellen. Das Menü ist nur verfügbar, solange das Formular aktiv ist. Daher müssen Sie, sobald das Formular deaktiviert wird, das Menü löschen. Ist beispielsweise VFPs Projekt-Manager das aktive Fenster, finden Sie in der Menüleiste das Menü „Projekt“. Ist der Projekt-Manager nicht das aktive Fenster, wird das Menü gelöscht. Wie verbinden Sie ein Menü mit einem Formular? 1. Erstellen Sie im Menü-Designer ein Menü mit den zu dem Formular gehörenden Einträgen. 2. Wenn der Menü-Designer aktiv ist, wählen Sie im Menü „Ansicht“ den Eintrag „Allgemeine Optionen...“ 3. Wählen Sie „Anfügen“, „Vor“ oder „Nach“, um die Positionierung des Menüs festzulegen. Wählen Sie nicht „Ersetzen“, da Sie sonst alle anderen Einträge der Menüleiste verlieren, wenn Ihr Formular ausgeführt wird. 4. Klicken Sie im Menü-Designer auf die Schaltfläche „Optionen“, um den Dialog „Optionen zur Bezeichnung“ zu öffnen. 5. Definieren Sie hier einen Tastentext, den Sie benötigen, wenn Sie den Eintrag aus der Menüleiste löschen. 6. Generieren Sie den Menücode. Das Menü mit dem Formular verbinden: 1. 2. Im Ereignis Activate des Formulars rufen Sie Ihr Menü auf. Befindet es sich beispielsweise in FORMMENU.MPR, benötigen Sie dafür folgenden Code: DO FormMenu.MPR In den Ereignissen Deactivate und Destroy des Formulars geben Sie das Menü wieder frei. Wenn Sie Ihr Menü myform genannt haben, schreiben Sie in beide Ereignisse: RELEASE PAD myform OF _MSYSMENU Anzeigen der zuletzt benutzten Dokumente im Menü Datei Windowsanwendungen zeigen im Menü Datei häufig eine Liste der zuletzt genutzten (MRU = most recently used) Dokumente an, so daß der Anwender schnell wieder zu einem Dokument zurückkehren kann, das er bearbeitet. Es ist relativ einfach, diese Funktionalität auch in eigene Anwendungen zu integrieren. Speichern der zuletzt genutzten Dokumente Um die zuletzt benutzten Formulare, Berichte oder Abfragen im Menü Datei anzuzeigen, müssen Sie Informationen über diese Dokumente in einer Tabelle speichern. Die folgende Tabelle speichert, welcher Eintrag im Menü angezeigt werden soll, die Aktion, die ausgeführt werden soll, wenn der Anwender den Eintrag auswählt, und den Zeitpunkt der letzten Nutzung der Dokumente. Wahrscheinlich haben Sie nicht vor, die letzten dreißig Formulare, Berichte oder Abfragen im Dateimenü anzuzeigen. Sie müssen daher die Anzahl der anzuzeigenden Einträge beschränken. Vier bis acht Einträge sind sinnvoll. Wenn der Anwender nun eine Abfrage, einen Bericht oder ein Formular ausführt, ändern Sie die Informationen in dieser Tabelle, in diesem Beispiel UPREFS.DBF, entsprechend ab. Dies wird im folgenden Code demonstriert. In diesem Code ist cFormName der Name des Formulars, das der Anwender ausführt, cAction ist eine Zeichenkette, die anzeigt, welche Aktion ausgeführt werden soll, wenn der Anwender den Menüeintrag auswählt, um das Formular erneut zu öffnen, und nMaxItems gibt die höchstmögliche Anzahl an Einträgen an, die Sie festgelegt haben. SELECT Uprefs LOCATE FOR prompt = cFormName IF FOUND() REPLACE Timestamp WITH DATETIME() ELSE IF RECCOUNT() < nMaxItems && maximal anzuzeigende Einträge INSERT INTO Uprefs VALUES(cFormName, cAction, DATETIME()) ELSE SET ORDER TO Timestamp ASCENDING && der älteste zuerst GO TOP REPLACE prompt WITH cFormName REPLACE Action WITH cAction REPLACE Timestamp WITH DATETIME() ENDIF ENDIF Sie müssen nach jeder Änderung der Tabelle das Menüprogramm erneut aufrufen: DO FILE.MPR Die MRU-Dokumente dem Dateimenü hinzufügen Wenn Sie im Menü-Designer das Menü Datei erstellen, legen Sie alle Einträge in das Menü fest mit Ausnahme des Separators und dem Eintrag Beenden am Ende des Menüs. Diese werden, sobald die MRU-Dokumente eingetragen sind, programmatisch hinzugefügt. Fügen Sie den folgenden Code Ihrer Abschlußprozedur hinzu. In diesem Code gibt die Variable nBar immer die Anzahl der definierten Einträge im Menü wieder. Die Variable iPrefix enthält die Ziffer, die neben dem Dokument angezeigt wird. nBar = CNTBAR("_MFILE") cOldAlias = ALIAS() IF !USED('UPrefs') USE UPrefs IN 0 ENDIF SELECT Uprefs * Sortieren, um sicherzustellen, daß das zuletzt benutzte Dokument am Anfang steht SET ORDER TO timestamp DESCENDING IF RECCOUNT() > 0 && es sind Dokumente gespeichert iPrefix = 0 nBar = nBar + 1 * Vor der MRU-Liste einen Separator anzeigen DEFINE BAR nBar OF _MFILE PROMPT "\-" SCAN nBar = nBar + 1 iPrefix = iPrefix + 1 cAction = ALLTRIM(UPrefs.Action) DEFINE BAR nBar OF _MFILE PROMPT "\<" + ; ALLTRIM(STR(iPrefix)) + " " + UPrefs.Prompt ON SELECTION BAR nBar OF _MFILE &cAction ENDSCAN ENDIF * Den Eintrag “Beenden” hinzufügen DEFINE BAR nBar + 1 OF _MFILE PROMPT "\-" DEFINE BAR nBar + 2 OF _MFILE PROMPT "\<Beenden" ON SELECTION BAR nBar + 2 OF _MFILE CLEAR EVENTS IF !EMPTY(cOldAlias) SELECT (cOldAlias) ENDIF Ausgehend von den zwei Einträgen in unserem Beispiel zeigt die folgende Abbildung das daraus resultierende Menü. Top-Level-Formularen Menüs hinzufügen Ein Top-Level-Formular (auch Single Document Interface oder SDI-Formular genannt) wird nicht vom Hauptfenster von Visual FoxPro abgeleitet. Sie erstellen ein Top-Level-Formular in Visual FoxPro 3.0, indem Sie die Eigenschaft Desktop des Formulars auf .T. setzen. Top-level-Masken sind aber erst in Visual FoxPro 5.0 wirklich eigenständige Fenster mit einem eigenen Eintrag in der Windows-Taskbar. In VFP 5.0 erstellen Sie ein Top-Level-Formular, indem Sie die Eigenschaft ShowWindow des Formulars auf „2 – As Top-Level Form“ setzen. Da das Top-Level-Formular nicht im Hauptfenster von VFP enthalten ist und daher nicht notwendigerweise Zugriff auf das Systemmenü hat, könnten Sie den Wunsch hegen, dem Formular ein Menü direkt hinzuzufügen. Der Menü-Designer von VFP 3.0 erlaubt es Ihnen nicht, ein Menü für ein Top-Level-Formular zu erstellen. Sie müssen daher den Code selbst schreiben. Der folgende Code kann dem Ereignis Init eines Formulars hinzugefügt werden, um ihm ein Menü hinzuzufügen: DEFINE MENU _example BAR IN WINDOW (THISFORM.Name) COLOR SCHEME 1 DEFINE DEFINE ON PAD ON PAD PAD p1 OF _example PROMPT "\<Datei" PAD p2 OF _example PROMPT "\<Farbe" p1 OF _example ACTIVATE POPUP file p2 OF _example ACTIVATE POPUP color DEFINE POPUP file MARGIN DEFINE BAR 1 OF file PROMPT "\<Beenden" ON SELECTION BAR 1 OF file _SCREEN.ActiveForm.Release DEFINE POPUP color MARGIN DEFINE BAR 1 OF color PROMPT "Vordergrund" DEFINE BAR 2 OF color PROMPT "Hintergrund" ON SELECTION BAR 1 OF color _SCREEN.ActiveForm.SetAll("ForeColor", GETCOLOR()) ON SELECTION BAR 2 OF color _SCREEN.ActiveForm.SetAll("BackColor", GETCOLOR()) ACTIVATE MENU _example NOWAIT In Visual FoxPro 5.0 können Sie die Menüs der Top-Level-Formulare mit dem Menü-Designer erstellen. Rufen Sie dafür im Menü Ansicht den Dialog „Allgemeine Optionen“ auf und klicken dann auf „Formular der obersten Ebene.“ Wenn Sie das Menü für ein Top-Level-Formular generieren, enthält der generierte Code Kommentare darüber, wie das Menü zu verwenden ist: @@@* To attach this menu to your Top-Level form, * call it from the Init event of the form: * Syntax: DO <mprname> WITH <oFormRef> [,<cMenuname>|<lRename>] * * * oFormRef - form object reference (THIS) cMenuname - name for menu lRename - renames Name property of your form * example: * * * PROCEDURE Init DO mymenu.mpr WITH THIS,.T. ENDPROC * * * * * * * * Use the optional 2nd parameter if you plan on running multiple instances of your Top-Level form. The logical lRename parameter will change the name property of your form to the same name given the menu and may cause conflicts in your code if you directly reference the form by name. You will also need to remove the menu when the form is destroyed so that it does not remain around in memory unless you wish to reactivate it later in a new form. * * * * If you passed the optional lRename parameter as .T. as in the above example, you can easily remove the menu in the form's Destroy event as shown below. This strategy is ideal when using multiple instances of Top-Level forms. * example: * * * PROCEDURE Destroy RELEASE MENU (THIS.Name) ENDPROC Erstellen von Shortcut-Menüs Ein Shortcut-Menü, auch Kontextmenü genannt, wird angezeigt, wenn der Anwender mit der rechten Taste auf ein Objekt klickt. Wenn Sie in Visual 5.0 ein neues Menü erstellen, haben Sie die Auswahl, ob Sie ein normales oder ein Shortcut-Menü erstellen möchten. In der Anwendung Solutions von VFP 5.0 sind zwei Beispiele für Shortcut-Menüs enthalten: „Anzeigen von Shortcut-Menüs“ und „Erstellen dynamischer Kontextmenüs“. Das Beispiel „Erstellen dynamischer Kontextmenüs“, das im Abschnitt „Erstellen von Shortcut-Menüs ohne den Menü-Designer“ näher beschrieben wird, benutzt die Codedefinition einer Hilfsklasse für Menüs, um das Shortcut-Menü zu erstellen. Mit kleinen Anpassungen kann der Code auch in VFP 3.0 verwendet werden. Anzeigen von Markierungszeichen neben den Einträgen der Shortcut-Menüs Sie können neben den Einträgen der Shortcut-Menüs dynamisch Markierungszeichen (Sie wissen schon: diese kleinen Häkchen) anzeigen lassen, indem Sie im Initialisierungscode des Menüs die Direktive #PREPOP und im Abschlußcode den Befehl SET MARK OF einsetzen. Die Präprozessoranweisung #PREPOP legt fest, daß der Abschlußcode des Menüs vor dem Befehl ACTIVATE POPUP generiert wird. Das Beispiel „Anzeigen von Shortcut-Menüs“ aus Solution zeigt Ihnen, wie Sie die Markierungszeichen in einem Kontextmenü implementieren können. Die folgende Codezeile gehört zum Ereignis RightClick des Beispielsformulars von „Anzeigen von ShortcutMenüs“: DO frmshort.mpr WITH THIS FRMSHORT ist das Kontextmenü. Der folgende Code stammt aus dem Initialisierungscode des Menüs: PARAMETER oREF #PREPOP Der Parameter oREF ermöglicht es Ihnen, Eigenschaften eines Objekts (in diesem Fall des Formulars) im Code des Menüs auszulesen und zu schreiben. Der folgende Code aus dem Abschlußcode setzt ein Markierungszeichen neben einem Menüeintrag, wenn die Eigenschaft AlwaysOnTop des Formulars auf .T. gesetzt ist. SET MARK OF BAR 4 OF frmshort TO oRef.AlwaysOnTop Der Code des Menüs enthält die folgenden Zeilen, die die Anzeige des Menüeintrags ändern wenn das Kontextmenü angezeigt wird: SET MARK OF BAR 4 OF frmshort TO oRef.AlwaysOnTop ACTIVATE POPUP frmshort Ohne die Anweisung #PREPOP würde folgender Code generiert: ACTIVATE POPUP frmshort SET MARK OF BAR 4 OF frmshort TO oRef.AlwaysOnTop Da die Ausführung des Codes durch den Befehl ACTIVATE POPUP unterbrochen wird, bis der Anwender seine Auswahl getroffen hat, wird der Befehl SET MARK OF erst ausgeführt, wenn das Shortcut-Menü nicht mehr angezeigt wird, was nicht unbedingt sonderlich sinnvoll ist. Erstellen von Shortcut-Menüs ohne den Menü-Designer Sie können Shortcut-Menüs auch ohne den Menü-Designer erstellen. Das Beispiel „Erstellen dynamischer Kontextmenüs“ demonstriert die Entwicklung eines Kontextmenüs, das durch ein Array gesteuert wird. Da das Menü durch die Daten gesteuert wird, ist es zur Laufzeit leichter zu konfigurieren. Das Vorgehen läuft sowohl in VFP 3.0 als auch unter 5.0. Um den folgenden Code in Visual FoxPro 3.0 auszuführen, müssen Sie allerdings das Schlüsselwort SHORTCUT aus dem Befehl DEFINE POPUP löschen. SHORTCUT wurde in Visual FoxPro 5.0 neu eingeführt, um das Standardverhalten von Windows 95 bei Kontextmenüs bereitzustellen. Das Ereignis RightClick des Beispiels enthält den folgenden Code. Das Objekt oMenuShortcut, das in diesem Beispiel referenziert wird, basiert auf VFPs Klasse menulib, die Sie in der Klassenbibliothek \SAMPLES\CLASSES\UTILITY.VCX finden. LOCAL laMenu[5] laMenu="" laMenu[1]="\<Zentrieren" laMenu[2]="\<Font..." laMenu[3]="\<Minimieren" laMenu[4]="\-" laMenu[5]="\<Beenden" THISFORM.oMenuShortcut.ShowMenu(@laMenu) DO CASE CASE BAR()=1 THISFORM.AutoCenter=.T. CASE BAR()=2 THISFORM.SetFont && eine benutzerdefinierte Methode CASE BAR()=3 THISFORM.WindowState=1 CASE BAR()=5 THISFORM.Release ENDCASE Die Methode ShowMenu() des Objekts oMenuShortCut (das auf der Klasse menulib basiert) enthält den folgenden Code: LPARAMETERS taMenu,tcOnSelection LOCAL lcOnSelection,lnMenuCount,lnCount,llDoubleArray LOCAL lcMenuItem,lcMenuSelection EXTERNAL ARRAY taMenu IF PARAMETERS()=0 OR TYPE("taMenu")#"C" RETURN .F. ENDIF lnMenuCount=0 lnMenuCount=ALEN(taMenu,1) IF lnMenuCount=0 RETURN .F. ENDIF llDoubleArray=(ALEN(taMenu,2)>0) ACTIVATE SCREEN DEACTIVATE POPUP _popShortcutMenu DEFINE POPUP _popShortcutMenu ; FROM MROW(),MCOL() ; MARGIN ; RELATIVE ; SHORTCUT FOR lnCount = 1 TO lnMenuCount lcMenuItem=IIF(llDoubleArray,taMenu[lnCount,1],taMenu[lnCount]) DEFINE BAR lnCount OF _popShortcutMenu PROMPT (lcMenuItem) ENDFOR ON SELECTION POPUP _popShortcutMenu DEACTIVATE POPUP _popShortcutMenu ACTIVATE POPUP _popShortcutMenu RELEASE POPUP _popShortcutMenu IF BAR()=0 RETURN .F. ENDIF IF llDoubleArray lcMenuSelection=taMenu[BAR(),2] IF NOT EMPTY(lcMenuSelection) AND TYPE("lcMenuSelection")=="C" lcOnSelection=ALLTRIM(lcMenuSelection) ENDIF IF EMPTY(lcOnSelection) lcOnSelection=ALLTRIM(IIF(EMPTY(tcOnSelection),"",tcOnSelection)) ENDIF ELSE lcOnSelection=ALLTRIM(IIF(EMPTY(tcOnSelection),"",tcOnSelection)) ENDIF IF EMPTY(lcOnSelection) RETURN .F. ENDIF &lcOnSelection Wenn der Anwender mit der rechten Maustaste auf das Formular klickt, wird das Shortcut-Menü durch das Array definiert und angezeigt. Erstellen von Menüklassen Menüs sind in Visual FoxPro keine Objekte. Sie können aber um die Menüdefinition herum Wrapper-Klassen schreiben, um auch dort bei den Menüs über die vertraute Objektschnittstelle zu verfügen. Es gibt unterschiedliche Implementierungen für Menüklassen. Im Codebook von Yair Alan Griver (Sybex-Verlag, 1995) finden Sie den Code und die Dokumentation für eine stabile Menüklasse. Weitere Menüklassen können Sie sich aus dem Internet herunterladen. Hier ein sehr einfaches Beispiel für die Erstellung und Benutzung von Menüklassen: PUSH MENU _MSYSMENU bar1 = CREATEOBJECT("mBar", bar2 = CREATEOBJECT("mBar", bar3 = CREATEOBJECT("mBar", pop1 = CREATEOBJECT("mPop", pop1.AddBar(bar1) pop1.AddBar(bar2) pop1.AddBar(bar3) pad1 = CREATEOBJECT("mPad", pad1.SetPopup(pop1) "Test1", "bar1", "MESSAGEBOX(PROMPT())") "Test2", "bar2", "MESSAGEBOX(PROMPT())") "Done", "bar3", "POP MENU _MSYSMENU") "pop1") "Pad1", "No message") * Class Definitions DEFINE CLASS mpad AS CUSTOM PadName = "" Message = "" PROCEDURE Init(cName, cMessage) THIS.PadName = cName THIS.Message = cMessage DEFINE PAD (cName) OF _MSYSMENU ; PROMPT cName MESSAGE cMessage ENDPROC PROCEDURE SetPopup(oPopup) cName = THIS.PadName cPopup = oPopup.PopName ON PAD (cName) OF _MSYSMENU ACTIVATE POPUP (cPopup) ENDPROC ENDDEFINE DEFINE CLASS mpop AS CUSTOM PopName = "" BarCount = 0 PROCEDURE Init(cName) THIS.PopName = cName DEFINE POPUP (cName) MARGIN ENDPROC PROCEDURE AddBar(oBar) cName = THIS.PopName cAction = oBar.Action THIS.BarCount = THIS.BarCount + 1 DEFINE BAR THIS.BarCount OF (cName) ; PROMPT oBar.Prompt ; MESSAGE oBar.Message ON SELECTION BAR THIS.BarCount OF (cName) &cAction ENDPROC ENDDEFINE DEFINE CLASS mBar AS CUSTOM Prompt = "" Message = "" Action = "" PROCEDURE Init(cPrompt, cMessage, cAction) THIS.Prompt = cPrompt THIS.Message = cMessage THIS.Action = cAction ENDPROC ENDDEFINE Erstellen datengesteuerter Menüs Datengesteuerte Menüs sind flexibler und zur Laufzeit besser zu konfigurieren. Sie können Ihre eigenen Prozeduren oder Klassen erstellen, die das Lesen von Daten aus einer oder aus mehreren Tabellen verwalten. Anschließend wird aus den Daten, die in der Tabelle gespeichert sind, das Menü erstellt. Das folgende Beispiel zeigt das Design eines vollständig durch Daten gesteuerten Menüsystems. Dieses Beispiel arbeitet mit einer Tabelle, die Systeminformationen für das Menü enthält. Zusätzlich gibt es eine getrennte Tabelle für jedes Popup. Die Tabelle mit den Systeminformationen heißt Menubar und wird hier definiert: CREATE TABLE MENUBAR ; (NPAD C(15), ; PROMPT C(25), ; MESSAGE M, ; POPNAME C(15), ; DBFNAME C(10)) Mögliche Werte für einige Datensätze in der Tabelle: NPAD PROMPT MESSAGE POPNAME _MSM_FILE \<Datei Öffnen, Schließen, _MFILE Speichern usw. FILE _MSM_EDIT \<Bearbeiten Kopieren, Ausschneiden, Einfügen usw. EDIT _MEDIT DBFNAME Dieses Beispiel benötigt eine getrennte Tabelle für jedes Menü, die alle Menüeinträge und die Informationen enthält, welche Aktion ausgeführt wird, wenn der Eintrag ausgewählt wird. Sie könnten beispielsweise eine Tabelle mit der folgenden Struktur benutzen: CREATE TABLE FILE ; (NBAR C(15), ; PROMPT C(25), ; MESSAGE M, ; SKIPFOR M, ; HOTKEY C(8), ; ACTION M) Einige mögliche Datensätze in der Tabelle File: NBAR PROMPT MESSAGE SKIPFO HOTKE ACTION R Y 1 \<Neu Neuer Kunde Ctrl+N DO NewCustomer 2 Ö\<ffnen Formular öffnen Ctrl+F DO FORM GETFILE(‘scx‘) Wenn Sie die Daten eingetragen haben, können Sie ein Programm oder eine Methode aufrufen, um die Daten auslesen und das Menü aufbauen zu lassen. Der folgende Code arbeitet sich durch die Tabelle Menubar und erstellt auf der Grundlage dieser Daten die Menüs. SELECT MenuBar SCAN DEFINE PAD (ALLTRIM(npad)) OF _MSYSMENU ; PROMPT ALLTRIM(Prompt) MESSAGE ALLTRIM(Message) DO DefinePop WITH popname, npad, dbfname ENDSCAN Die Prozedur DefinePop erstellt ein Popup, verbindet es mit dem dazugehörigen Eintrag auf der Menüleiste und definiert alle Einträge. PROCEDURE DefinePop LPARAMETERS cPopup, cPadName, cTable cPopup = ALLTRIM(cPopup) cPadName = ALLTRIM(cPadName) cTable = ALLTRIM(cTable) cOldAlias = ALIAS() DEFINE POPUP (cPopup) RELATIVE MARGIN ON PAD (cPadName) OF _MSYSMENU ACTIVATE POPUP (cPopup) LOCAL cAction, cPad, cKey, cDefineString IF !USED(cTable) USE (cTable) IN 0 ENDIF SELECT (cTable) SCAN cAction = ALLTRIM(action) cBar = ALLTRIM(nBar) cKey = ALLTRIM(hotkey) cSkipFor = ALLTRIM(skipfor) cDefineString = "DEFINE BAR " + cBar + ; " OF " + cPopup + ; " PROMPT '" + ALLTRIM(prompt) + "'" IF !EMPTY(cKey) cDefineString = cDefineString + " KEY " + cKey ENDIF IF !EMPTY(cSkipFor) cDefineString = cDefineString + " SKIP FOR " + cSkipFor ENDIF &cDefineString ON SELECTION BAR &cBar of (cPopup) &cAction ENDSCAN IF !EMPTY(cOldAlias) SELECT (cOldAlias) ENDIF RETURN Wenn Sie eine datengesteuerte Menüengine wie diese einsetzen wollen, erstellen Sie am besten eine Formularklasse mit Methoden, die den Definitionscode des Menüs verwalten. Anschließend erstellen Sie ein Formular basierend auf dieser Klasse und setzen die Eigenschaft DataSession des Formulars auf 2 – Private Data Session. Wenn Sie das Formular in Ihrer Anwendung ausführen, benutzen Sie das Schlüsselwort NOSHOW, damit das Formular zwar aufgebaut, aber nicht angezeigt, wird. Beispiel: DO FORMmenuform NOSHOW. Dann rufen Sie die Methoden des Formulars auf, die das Menüsystem verwalten. Über den Autor: Rodney Hill ist bei der Microsoft Corporation in den USA beschäftigt. Die Abdruckrechte wurden von Microsoft eingeräumt. Eine Danksagung geht an Randy Brown, Tom Cooper und Ken Levy, welche diesen Artikel gegengelesen, fehlende Informationen aufgezeigt und Änderungen angeregt haben. Liz Ruest hat den Artikel überarbeitet. Microsoft, Windows und Visual FoxPro sind registrierte Warenzeichen der Microsoft Corporation in den USA und in anderen Ländern. Andere Produkt- und Firmennamen können Warenzeichen der jeweiligen Eigentümer sein.