4.6 Dialog4.6.1 Überblick ^4.6.1 ÜberblickDialoge sind unabhängige Fenster, mit denen der Nutzer interagieren kann. Sie werden für die unterschiedlichsten Aufgaben eingesetzt (siehe Bilder). Deshalb verfügt das Dialog-Objekt über sehr viele Fähigkeiten, die in den folgenden Abschnitten beschrieben werden.
Focus und TargetDialog-Objekte sind ein Knoten in der Focus- und Target-Hierarchie. Es ist möglich zu überwachen, ob ein Dialog-Objekt den Focus oder das Target hat, indem man einen Focus- bzw. Target-Handler schreibt. Die notwendigen Details zur Arbeit mit Focus und Target finden Sie im Kapitel 12 (Focus und Target) des Handbuchs "Spezielle Themen". Das Arbeiten mit Focus und Target ist etwas für erfahrene Programmierer und nur in wenigen Fällen notwendig. Eine Ausnahme bildet die Implementation von speziellen Menüs wie dem "Bearbeiten" Menü. Diesem Thema ist deswegen ein eigenes Kapitel ("Spezielle Themen", Kapitel 13) gewidmet. Abstammung:
Die Möglichkeiten, einen Dialog einzusetzen sind sehr vielfältig. In diesem Zusammenhang sollten Sie die folgenden Begriffe kennen:
Im Folgenden finden Sie eine vollständige Liste der Instance-Variablen, Methoden, Handler-Typen und Routinen eines Dialog-Objekts. Eine Beschreibung dieser finden Sie in den folgenden Kapiteln. Spezielle Instance-Variablen:
Methoden:
Action-Handler-Typen:
Spezielle Routinen: OpenBlockingDialog(dialogObj as object) ^4.6.2 Allgemeine Eigenschaften
MakeResizableNormalerweise sind Dialogboxen nicht größenveränderlich. Der Hint MakeResizable bewirkt, dass man die Größe des Dialogs auf dem Bildschirm verändern kann. Syntax UI-Code: MakeResizable NoFocusNormalerweise übernimmt ein Dialog, wenn er geöffnet wird, oder der Nutzer mit der Maus ein Objekt im Dialog anklickt, automatisch den Focus, d.h. alle folgenden Tastatureingaben gehen an den Dialog. In Situationen, in denen dieses Verhalten störend ist, können Sie mit dem Hint NoFocus verhindern, dass der Dialog Tastatureingaben entgegennimmt. Wenn der Nutzer z.B. gerade einen Text eingibt, kann ein solcher Dialog mit der Maus bedient werden, ohne dass der Text den Focus verliert. Syntax UI-Code: NoFocus attrsDie Instance-Variable attrs speichert Attribute (spezielle Eigenschaften) des Dialogs. Dabei stehen die in der Tabelle aufgeführten Werte zur Verfügung. Sie werden weiter unten, an passender Stelle, ausführlich beschrieben.
Syntax UI-Code: attrs = numWert
Lesen: <numVar> = <obj>.attrs
Schreiben: <obj>.attrs = numWert
numWert ist eine der DA_-Konstanten oder Null
modal
Der Begriff "Modalität", beschreibt, inwieweit Eingaben (Tastatur und Maus) exklusiv an die Dialogbox gehen sollen. Bei einem nicht-modalen Dialog (NON_MODAL) können Sie beliebig zwischen der Dialogbox und dem Rest der Applikation oder des Systems hin- und her wechseln. Ein Beispiel wäre der Dialog "Linienattribute" aus GeoDraw. Ein Application-modaler Dialog (APP_MODAL) blockiert den Rest der Applikation, andere Anwendungen lassen sich weiter bedienen. Beispielsweise ist der "Speichern unter" Dialog von GeoWrite Application-modal. System-modale Dialoge (SYS_MODAL) blockieren die Bedienung des gesamten restlichen GEOS-Systems. Ein Beispiel ist die in bestimmten Situationen von GEOS erzeugte Nachfrage, ob das System heruntergefahren werden soll.
Syntax UI-Code: modal = numWert
Lesen: <numVar> = <obj>.modal
Schreiben: <obj>.modal = numWert
numWert ist eine der ~_MODAL-Konstanten von oben
isOpenDiese nur-Lesen Variable enthält die Information, ob der Dialog aktuell offen ist (auf dem Schirm sichtbar, isOpen = TRUE) oder nicht (isOpen = FALSE). Syntax Lesen: <numVar> = <obj>.isOpen ^4.6.3 Öffnen und Schließen von DialogboxenDamit ein Dialog auf dem Bildschirm erscheinen kann (geöffnet werden kann), muss er grundsätzlich in den generic Tree des Programms eingebunden sein. Es ist ein häufiger Fehler, dies bei Dialogen, nicht nach Methode 1 (siehe unten) verwendet werden, zu vergessen. R-BASIC verfügt prinzipiell über 3 Methoden, einen Dialog zu öffnen. Methode 1 Die einfachste Methode, einen Dialog zu verwenden, ist ihn als Child eines Menüs in den generischen Tree einzubinden. Das GEOS-System erzeugt dann automatisch einen Button im Menü, der den Dialog öffnet. Beispiel:
UI-Code-Fragment: Ausführliche Code-Beispiele finden Sie im Kapitel 4.6.3. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Menu DemoMenu Caption$ = "Demo .. " Children = Readbutton, Writebutton, RegisterDialog END Object Dialog RegisterDialog Caption$ = "Registrieren" Children = SerialText, OKButton, CancelButton END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Tipp: Wenn es zeitweise keinen Sinn macht, dass der Nutzer den Dialog öffnen kann, setzen Sie ihn zwischenzeitlich auf "not enabled" (<dialogObj>.enabled = FALSE).
Methode 2
Die nach Methode 1 eingebundenen Dialoge kann der Nutzer prinzipiell jederzeit öffnen. Oftmals ist es aber nötig, Dialoge vom Programm aus zu öffnen, wenn es die Situation erfordert. Für diesen Zweck verfügten Dialog-Objekte über Methoden zum Öffnen und Schließen der Dialogbox.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attrs = DA_HIDDEN_UNTIL_OPENED | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Der Dialog wird solange vor dem Nutzer versteckt (engl. hidden = versteckt) bis er explizit durch eine der Anweisungen Open oder OpenNoDisturb geöffnet wird ( until = bis, opened = geöffnet).
UI-Code-Fragment: Der Dialog erscheint nicht im Menü! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog RegisterDialog Caption$ = "Registrieren" Children = SerialText, OKButton, CancelButton attrs = DA_HIDDEN_UNTIL_OPENED ' Dialog verstecken END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
BASIC-Code-Fragment: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IF registerNr <> validNr THEN RegsiterDialog.Open | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Beachten Sie, dass R-BASIC nach dem Öffnen des Dialogs sofort mit der Abarbeitung der nächsten Codezeilen fortsetzt. Es wird nicht gewartet bis der Dialog wieder geschlossen wird. Ist das nicht gewollt, verwenden Sie bitte Methode 3.
Methode 3
Oftmals ist es für den Programmablauf erforderlich, dass der Nutzer zuerst den Dialog bedient, bevor die Programmabarbeitung fortgesetzt werden kann. Ein Beispiel wäre die Nachfrage, ob die Daten gespeichert werden sollen oder nicht. Für diesen Zweck gibt es die Funktion OpenBlockingDialog(), die auf eine Eingabe des Nutzers wartet und solange die weitere Programmabarbeitung blockiert ("Blocking"). Sie liefert einen numerischen Wert zurück, je nachdem, welchen Button des Dialogs der Nutzer gedrückt hat. Solche Dialoge müssen | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attrs = DA_BLOCKING | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
gesetzt haben. DA_BLOCKING impliziert DA_HIDDEN_UNTIL_OPENED, d.h. das System erzeugt keinen "Aktivierung-Button". Vergessen Sie aber nicht, den Dialog an irgendeiner Stelle in den generic Tree einzubinden.
Außerdem ist es erforderlich, dass ein Blocking-Dialog modal ist (APP_MODAL oder SYS_MODAL). Geben Sie keinen Wert vor, setzt R-BASIC automatisch modal = APP_MODAL.
Eine ausführliche Beschreibung der Arbeit mit Blocking-Dialogen und passende Code-Beispiele finden Sie im Kapitel 4.6.7
4.6.4 Behandlung von Messages^4.6.4.1 Messages von UI-ObjektenUI-Objekte, die sich in einem Dialog befinden (Buttons, Listen, InputLine-Texte...) können grundsätzlich Messages versenden (d.h. ihre Action-Handler aufrufen). Ebenso kann man mit diesen Objekten arbeiten (z.B. Instance-Variablen belegen oder abfragen), wenn der Dialog nicht offen (nicht auf dem Schirm) ist. Eine Ausnahme gibt es bei Blocking-Dialogen. Objekte in Blocking-Dialogen dürfen keine Action-Handler haben. Details dazu finden Sie im Kapitel 4.6.7.
4.6.4.2 InteractionCommand
Die Instance-Variable interactionCommand dient der direkten Kommunikation zwischen Button-Objekten in einen Dialog und dem Dialog-Objekt selbst, ohne dass Sie als R-BASIC Programmierer eingreifen müssen. Dies wird für Standard-Dialoge und für Blocking-Dialoge benötigt. Ein interactionCommand-Wert ist eine Zahl (Datentyp WORD). Er wird vom Button an sein Dialog-Objekt gesendet, wenn er angeklickt wird. Dadurch kann der Dialog bestimmte Aktionen automatisch ausführen, z.B. sich selbst schließen oder einen seiner Action-Handler aufrufen (OnOpen, OnClose oder OnCommand, siehe Kapitel 4.6.4.3). Zu diesem Zweck besitzen sowohl Buttons als auch Dialoge eine Instance-Variable namens interactionCommand. Den interactionCommand-Wert eines Buttons kann man lesen und schreiben, üblicher Weise wird er im UI-Code gesetzt. Den interactionCommand-Wert eines Dialogs kann man nur lesen. Er wird vom Dialog automatisch belegt. Beim Öffnen des Dialogs wird der Wert auf Null gesetzt. Syntax Lesen numVar = <dialogObj>.interactionCommand Die folgenden Werte sind vom System definiert. Sie können auch eigene Werte definieren. Eigene interactionCommand-Werte müssen größer als 999 sein, die Werte 0 bis 999 sind vom System reserviert!
Intern passiert folgendes: Nehmen wir an, wir haben einen Button in einem Dialog-Objekt, dessen interactionCommand-Wert belegt ist. Wird dieser Button angeklickt, so sendet er seinen interactionCommand-Wert direkt an den Dialog. Der Dialog reagiert darauf in Abhängigkeit vom interactionCommand-Wert: IC_CLOSE:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog HelpedDialog caption$ = "Dialog mit Hilfe" children = ... , DilaogHelpButton dialogtype = DT_COMMAND helpContext$="MoreHelp" End Object ... Button DilaogHelpButton Caption$ = "Hilf mir" interactionCommand = IC_HELP placeObject = REPLY_BAR End Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
^4.6.4.3 Behandlung von Dialog-Messages
Action-Handler-Typen:
Aus Sicht einer Dialogbox gibt es drei wichtige Ereignisse:
Syntax UI-Code: OnOpen = <Handler>
OnClose = <Handler>
OnCommand = <Handler>
Schreiben: <obj>.OnOpen = <Handler>
<obj>.OnClose = <Handler>
<obj>.OnCommand = <Handler>
OnOpenDer OnOpen-Handler wird gerufen, wenn die Dialogbox geöffnet wird. OnOpen-Handler müssen als DialogAction definiert sein, wobei der Parameter command unbestimmt ist und nicht verwendet werden sollte. Zum Zeitpunkt, an dem der R-BASIC-Handler ausgeführt wird, ist der Dialog bereits auf dem Schirm.
Syntax UI-Code: OnOpen = <Handler>
Schreiben: <obj>.OnOpen = <Handler>
Beispiel UI-Code: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog CommandDialog Caption$ = "Persönliche Daten" Children = NameText, VornameText, OKButton justifyChildren = J_CENTER_ON_CAPTION OnOpen = PslDataOpen END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Dazugehöriger BASIC-Code | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogAction PslDataOpen <.. Was immer hier zu tun ist ...> END Action | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
OnCloseDer OnClose-Handler wird gerufen, wenn die Dialogbox geschlossen wird. OnClose-Handler müssen als DialogAction definiert sein, wobei der Parameter command immer 1 (IC_CLOSE) ist. Zum Zeitpunkt, an dem der R-BASIC-Handler ausgeführt wird, ist der Dialog bereits nicht mehr auf dem Schirm.
Syntax UI-Code: OnClose = <Handler>
Schreiben: <obj>.OnClose = <Handler>
Beispiel UI-Code: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog CommandDialog Caption$ = "Persönliche Daten" Children = NameText, VornameText, OKButton justifyChildren = J_CENTER_ON_CAPTION OnClose = PslDataClose END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dazugehöriger BASIC-Code: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogAction PslDataClose <.. Was immer hier zu tun ist ...> END Action | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
OnCommandDer OnCommand-Handler wird gerufen, wenn ein Button mit einem interactionCommand-Wert, der nicht IC_CLOSE ist, angeklickt wurde (IC_CLOSE ruft den OnClose-Handler). OnCommand-Handler müssen als DialogAction definiert sein, wobei der Parameter "command" den interactionCommand-Wert des auslösenden Buttons enthält.
Syntax UI-Code: OnCommand = <Handler>
Schreiben: <obj>.OnCommand = <Handler>
Ausführliches Beispiel. Eine Dialog-Box mit 3 Schaltern. UI-Code:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog ComplexDialog Caption$ = "Namen eingeben" Children = NameText, VornameText, MyReplyBar justifyChildren = J_CENTER_ON_CAPTION OnCommand = CommandHandler END Object InputLine NameText Caption$ = "Name:" end object InputLine VornameText Caption$ = "Vorname:" end object Group MyReplybar MakeReplyBar Children = CloseButton, OKButton, DeleteButton END Object Button CloseButton Caption$ = "Schließen" interactionCommand = IC_CLOSE END Object Button DeleteButton Caption$ = "Löschen" interactionCommand = 1001 ' eigener Wert END Object Button OKButton Caption$ = "Übernehmen" interactionCommand = IC_OK END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Im BASIC-Code muss nur der OnCommand-Handler vereinbart werden. Der CloseButton wird vom Dialog automatisch bedient, da er als interactionCommand IC_CLOSE gesetzt hat. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DialogAction CommandHandler
IF command = 1001 THEN ' Texte löschen
NameText.text$ = ""
VornameText.text$ = ""
END IF
IF command = IC_OK THEN
MsgBox "Werte werden übernommen"
ComplexDialog.Close
END IF
END Action
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
^4.6.5 Frei definierte Dialoge
Der wohl am einfachsten nachvollziehbare Weg, eine Dialogbox aufzubauen, ist, alle UI-Objekte selbst zu definieren, wie das folgende Beispiel zeigt:
UI-Code: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Menu MainMenu Caption$ = "Demo .. " Children = Readbutton, Writebutton, RegisterDialog END Object <.. ReadButton und WriteButton nicht aufgeführt..> !***************************************** ! Demo-Dialog !***************************************** Dialog RegisterDialog Caption$ = "Registrieren" Children = SerialText, MyReplyBar END Object InputLine SerialText Caption$ = "Seriennummer:" justifyCaption = J_TOP END Object Group MyReplyBar MakeReplyBar Children = OKButton, CancelButton END Object Button OKButton Caption$ = "OK" ActionHandler = RegisterOK END Object Button CancelButton Caption$ = "Abbrechen" ActionHandler = RegisterCancel END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Im Dialog-Objekt ist kein Wert für dialogType gesetzt, er steht per default auf DT_NORMAL.
Öffnet der Nutzer den Dialog kann er eine Seriennummer eingeben und dann auf OK oder Abbrechen klicken. Dadurch werden die Action-Handler RegisterOK bzw. RegisterCancel aufgerufen. Diese könnten so aussehen: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ButtonAction RegisterOK RegisterDialog.Close MsgBox "Registrierung erfolgreich" END Action ButtonAction RegisterCancel RegisterDialog.Close END Action | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Eine Behandlung der eingegeben Seriennummer wurde der Übersichtlichkeit halber ausgelassen. Wichtig ist, dass Sie die Dialogbox in beiden Fällen manuell schließen müssen. Das ist schon alles.
Der Dialog im Beispiel besteht aus 5 Objekten: dem Dialog-Objekt, einem Text-Objekt, einer ReplyBar und zwei Buttons. Reply-Bars sind typisch für Dialoge, deswegen kann das System automatisch eine Reply-Bar anlegen, wenn wir sie anfordern. Dazu muss man nur:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog RegisterDialog Caption$ = "Registrieren" Children = SerialText, OKButton, CancelButton END Object InputLine SerialText Caption$ = "Seriennummer:" justifyCaption = J_TOP END Object Button OKButton placeObject = REPLY_BAR Caption$ = "OK" ActionHandler = RegisterOK END Object Button CancelButton placeObject = REPLY_BAR Caption$ = "Abbrechen" ActionHandler = RegisterCancel END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
^4.6.6 Standard-Dialoge
Auch wenn es möglich ist, die UI-Objekte im Dialog vollständig selbst zu definieren, ist dies in vielen Fällen gar nicht nötig. Man kann stattdessen sogenannte "Standard-Dialoge" verwenden, die bereits einige vorgefertigte Objekte enthalten. Dies sind ein oder mehrere Buttons - Standard-Buttons genannt - und eine Reply-Bar. Da die Buttons direkt vom System erzeugt werden, können wir ihnen keinen Action-Handler zuweisen. Stattdessen belegt GEOS die Instance-Variable interactionCommand des Buttons. Um einen Standard-Dialog zu verwenden benötigt man nur eine einzige Zeile im UI-Code: Die Belegung der Instance-Variablen dialogType.
Syntax UI-Code: dialogType = numWert
Lesen: <numVar> = <obj>.dialogType
Schreiben: <obj>.dialogType = numWert
Welche Buttons mit welchem interactionCommand-Wert erzeugt werden, hängt nur von der Belegung dieser Instance-Variablen ab. Die folgende Tabelle enthält die möglichen Werte für die Instance-Variable dialogType sowie die erzeugten Buttons und die zugeordneten interactionCommand-Werte.
Die genaue Beschriftung der Buttons kann je nach System-Version geringfügig variieren. Wenn nicht explizit anders angegeben wird die Dialogbox automatisch geschlossen, wenn der Nutzer auf einen der vom System erzeugten Buttons klickt. Die Verwendung der einzelnen dialogTypen und welche konkreten zusätzlichen Eigenschaften der Dialog dadurch erhält, wird in den nächsten Kapiteln ausführlich beschrieben.
4.6.6.1 Command-Dialoge
Ein Command-Dialog (engl.: command = Kommando, Anweisung) erzeugt automatisch eine Reply-Bar mit einen Schließen-Button.
Beispiel UI-Code: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog CommandDialog
Caption$ = "Persönliche Daten"
Children = NameText, VornameText
justifyChildren = J_CENTER_ON_CAPTION
dialogType = DT_COMMAND
END Object
InputLine NameText
Caption$ = "Name:"
END Object
InputLine VornameText
Caption$ = "Vorname:"
END Object
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
^4.6.6.2 Notification-Dialoge
Notification-Dialoge (Hinweis-Dialoge) erzeugen automatisch eine Reply-Bar mit einen OK-Button. Sie werden sehr häufig für "Blocking-Dialoge" (attrs = DA_BLOCKING, Kapitel 4.6.7) oder für den "Information über.."-Dialog im Dateimenü verwendet. Bevor Sie einen Notification-Dialog programmieren sollten sie prüfen, ob einer der R-BASIC-Befehle MsgBox, ErrorBox oder WarningBox nicht bereits Ihren Anforderungen genügt. Sie sind intern als Blocking-Dialog mit dialogType = DT_NOTIFICATION realisiert.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog NotificationDialog
Caption$ ="Notiz"
Children = NotificationText
dialogType = DT_NOTIFICATION
END Object
Memo NotificationText
text$ = "Es hat geklappt!"
readOnly = TRUE
END Object
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
^4.6.6.3 Question-Dialoge
Question-Dialoge (Frage-Dialoge) erzeugen automatisch eine Reply-Bar mit einen "Ja" und einem "Nein"-Button. Sie werden sehr häufig für "Blocking-Dialoge" (attrs = DA_BLOCKING, Kapitel 4.6.7) verwendet. Bevor Sie einen Question-Dialog programmieren sollten sie prüfen, ob der R-BASIC-Befehl QuestionBox nicht bereits Ihren Anforderungen genügt. QuestionBox ist intern als Blocking-Dialog mit dialogType = DT_QUESTION realisiert.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog QuestionDialog
Caption$ ="Frage"
Children = QuestionText
dialogType = DT_QUESTION
attrs = DA_BLOCKING
END Object
Memo QuestionText
text$ = "\r\tWirklich?\r"
readOnly = TRUE
END Object
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Tipp: Um herauszubekommen, ob der Nutzer auf "Ja" oder "Nein" geklickt hat können Sie den Dialog entweder als Blocking-Dialog programmieren (siehe Beispielcode und Kapitel 4.6.7) oder Sie verwenden den OnCommand-Handler des Dialogs (siehe Kapitel 4.6.4.3).
^4.6.6.4 Progress-Dialoge
Progress-Dialoge dienen dazu, dem Nutzer den Fortschritt einer Operation anzuzeigen und ein Abbrechen der Operation zu ermöglichen. Sie erzeugen automatisch eine Reply-Bar mit einen "Anhalten"-Button. Im Beispiel wird zur Fortschrittsanzeige ein Textobjekt verwendet.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog ProgressDialog Caption$ = "In Arbeit ..." Children = ProgressTex dialogType = DT_PROGRESS attrs = DA_HIDDEN_UNTIL_OPENED END Object Memo ProgressText Caption$ = "Fortschritt: " readOnly = TRUE fixedSize = 20 + ST_AVG_CHAR_WIDTH, 1 + ST_LINES_OF_TEXT END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Die korrekte Verwendung eines Progress-Dialogs erfordert etwas Aufmerksamkeit und Hintergrundwissen. GEOS ist ein Multithread-System, d.h. mehrere Prozesse (Threads) laufen quasi gleichzeitig ab. Selbst in einem R-BASIC-Programm gibt es zwei Threads - einen für den von Ihnen geschriebenen BASIC-Code und den UI-Thread, der die UI-Objekte bedient. Wenn Sie eine langwierige Operation ausführen wollen, z.B. das Suchen nach einer Datei, schreiben Sie dazu eine R-BASIC-Routine. Während diese läuft kann sie nicht durch das Anklicken eines Buttons unterbrochen werden, da der Action-Handler dieses Buttons auch im BASIC-Code-Thread läuft. Das Ereignis (Anklicken des Schalters "Abbrechen") würde vom System in eine Warteschlage gestellt und abgearbeitet, wenn die Suchroutine fertig ist. Sie können auf diese Weise also eine laufende Operation nicht unterbrechen.
An dieser Stelle kommt der UI-Thread und die Dialog-Instance-Variable interactionCommand ins Spiel. Klickt der Nutzer auf den "Anhalten" Button, so sendet dieser im UI-Thread - also parallel zur laufenden Suchroutine - eine Message an den Dialog. Der Dialog schließt sich uns setzt seine Instance-Variable interactionCommand auf den vom Button gesendeten Wert - in diesem Fall IC_STOP. Wenn Sie während der laufenden Operation regelmäßig die interactionCommand-Variable des Progress-Dialogs abfragen, können Sie den Prozess auf Anforderung abbrechen. Das folgende Code-Fragment zeigt, wie das geht: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ProgressDialog.Open ' Dialog anzeigen
FOR N = 1 TO 100
ProgressText.Text$ = Str$(n) + " %" ' Fortschritt melden
< ... Nächsten Schritt der Operation durchführen .. >
IF ProgressDialog.interactionCommand = IC_STOP THEN BREAK
NEXT N
ProgressDialog.Close ' Dialog schließen
IF ProgressDialog.interactionCommand = IC_STOP THEN
MsgBox "Abgebrochen"
END IF
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Tipp: Wenn es im konkreten Fall störend ist, dass sich der Dialog sofort selbständig schließt können Sie statt eines Progress-Dialogs einen frei definierten Dialog mit einem eigenen CancelButton verwenden. Dieser setzt zwar die interactionCommand-Variable des Dialogs, schließt ihn aber nicht, da er kein Standard-Button ist. Das ist im folgenden Beispiel gezeigt.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog ProgressDialog Caption$ = "In Arbeit ..." Children = ProgressText, CancelButton attrs = DA_HIDDEN_UNTIL_OPENED END Object Memo ProgressText Caption$ = "Fortschritt: " readOnly = TRUE fixedSize = 20 + ST_AVG_CHAR_WIDTH, 1 + ST_LINES_OF_TEXT END Object Button CancelButton Caption$ = "Anhalten" placeObject = REPLY_BAR interactionCommand = IC_STOP END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sie dürfen nur nicht vergessen, den Dialog manuell mit ProgressDialog.Close zu schließen.
Anstelle eine Progress-Dialogs können Sie eventuell auch einen Button verwenden, dessen Instance-Variable unhandledEvents Sie abfragen. Das ist im Abschnitt 4.3 beschrieben.
4.6.6.5 Dialoge im Delayed Mode
Der Dialogtyp DT_DELAYED_APPLY erzeugt eine Dialog-Box die im sogenannten "Delayed Mode" arbeitet, ganz so als würden Sie den Hint MakeDelayedApply für den Dialog setzen. Der Delayed Mode ist ausführlich im Kapitel 3.4.2 beschrieben. Im Kern besteht er in Folgendem:
Beispiel: Eine Dialogbox enthält eine Liste und ein Number-Objekt, die sich gegenseitig über Status-Handler auf dem neuesten Stand halten. Beim Klick auf "Anwenden" wird der ApplyHandler der Liste aufgerufen.
UI-Code: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog DialogInDelayedMode caption$ ="Eigenschaften auswählen" dialogType = DT_DELAYED_APPLY Children = DList, DNum orientChildren = ORIENT_VERTICALLY justifyChildren = J_CENTER end object RadioButtonGroup DList Children = rb0,rb1, rb2, rb3, rb4, rb5 OrientChildren = ORIENT_HORIZONTALLY selection = 3 MakeToolbox StatusHandler = DListStatusChanged ApplyHandler = DListApply END Object RadioButton rb0: Caption$ = " - 0 - ": identifier = 0: END Object RadioButton rb1: Caption$ = " - 1 - ": identifier = 1: END Object RadioButton rb2: Caption$ = " - 2 - ": identifier = 2: END Object RadioButton rb3: Caption$ = " - 3 - ": identifier = 3: END Object RadioButton rb4: Caption$ = " - 4 - ": identifier = 4: END Object RadioButton rb5: Caption$ = " - 5 - ": identifier = 5: END Object Number DNum Caption$ = "Select:" StatusHandler = DNumStatusChanged minVal = 0 : maxVal = 5 value = 3 END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BASIC-Code. Die Zeile "DList.modified = TRUE" sorgt dafür, dass die Liste als "geändert" markiert wird, da sie sonst ggf. ihren Apply-Handler nicht aufruft: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
LISTACTION DListApply MsgBox Str$(selection)+ " ist selektiert" END Action LISTACTION DListStatusChanged DNum.value = selection END Action NUMBERACTION DNumStatusChanged DList.selection = value DList.modified = TRUE END Action | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Eine mögliche Ergänzung wäre, einen zusätzlichen Button "Zurücksetzen" hinzuzufügen, der die Liste und das Number-Objekt auf den Anfangsbestand zurücksetzt. Wie das gemacht wird, wird am Ende des nächsten Abschnitts (4.6.6.6) beschrieben.
^4.6.6.6 Eigene Buttons in Standard-DialogenSie können sowohl eigene Buttons zur Reply-Bar der Standard-Dialoge hinzufügen als auch die vorhandenen durch eigene ersetzen. Letzteres kann z.B. sinnvoll sein, wenn Sie die Beschriftung der Buttons ändern oder durch eine Grafik ersetzen wollen. In jedem Fall muss ein solcher Button die Zeile | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
placeObject = REPLY_BAR | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
und einen interactionCommand-Wert enthalten. Existiert schon ein Standard-Button mit diesem interactionCommand-Wert, so wird er ersetzt, andernfalls wird ein weiterer Button hinzugefügt. Für ihren eigenen Button können Sie einen der vorhandenen interactionCommand-Werte verwenden (z.B. IC_OK) oder einen eigenen definieren. Eigene interactionCommand-Werte müssen größer als 999 sein, die Werte 0 bis 999 sind vom System reserviert!
Beispiel: Der Command-Dialog aus dem letzten Abschnitt war so definiert (die Text-Objekte sind nicht mit aufgeführt): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog CommandDialog Caption$ = "Persönliche Daten" Children = NameText, VornameText dialogType = DT_COMMAND justifyChildren = J_CENTER_ON_CAPTION END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Der Button mit der Aufschrift "Schließen" ist vom System erzeugt und entsprechend der Tabelle vorn (Anschnitt 4.6.6) mit dem interactionCommand IC_CLOSE belegt.
Um die Beschriftung des Buttons von "Schließen" auf "Abbrechen" zu ändern und einen weiteren Button mit der Aufschrift "Übernehmen" hinzuzufügen muss man folgendes tun (die Text-Objekte sind wieder nicht mit aufgeführt): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog CommandDialog Caption$ = "Persönliche Daten" Children = NameText, VornameText, CloseButton, OKButton justifyChildren = J_CENTER_ON_CAPTION dialogType = DT_COMMAND OnCommand = DialogOKHandler END Object Button CloseButton Caption$ = "Abbrechen" interactionCommand = IC_CLOSE ' Button wird ersetzt placeObject = REPLY_BAR END Object Button OKButton Caption$ = "Übernehmen" interactionCommand = IC_OK ' Button wird hinzugefügt placeObject = REPLY_BAR END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Die beiden neuen Buttons bekommen einen interactionCommand-Wert, aber keinen ActionHandler. Um auf den OK-Button regieren zu können, bekommt der Dialog einen Action-Handler (OnCommand = DialogOKHandler). Dieser muss mindestens den Dialog schließen, da nicht-Standard-Buttons dies nicht automatisch tun.
Im BASIC-Code muss nur der OnCommand-Handler vereinbart werden: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DIALOGACTION DialogOKHandler CommandDialog.Close < .. Auswertung des Namens hier ..> END Action | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Ein weiterer häufiger Fall für einen eigenen Button in einem Dialog-Objekt ist ein "Reset"-Button. Dafür kann man das InteractionCommand IC_RESET verwenden. Der Dialog muss dann einen OnCommand-Handler haben, der dieses Kommando auswertet und alle betroffenen Objekte auf ihren Anfangswert setzt.
Beispiel: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
DIALOGACTION MyOnCommandHandler
IF command = IC_RESET THEN
NameText.text$ = ""
VornameText.text$ = ""
END IF
< ... >
END Action
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
^4.6.7 Arbeit mit Blocking-Dialogen
Oftmals ist es für den Programmablauf erforderlich, dass der Nutzer zuerst den Dialog bedient, bevor die Programmabarbeitung fortgesetzt werden kann. Das heißt, die weitere Programmabarbeitung wird solange blockiert ("Blocking"), bis der Dialog beendet ist. Ein Beispiel wäre die Nachfrage, ob die Daten gespeichert werden sollen oder nicht. Für Blocking-Dialoge kann man sowohl frei definierte als auch Standard-Dialoge verwenden.
attrs = DA_BLOCKINGEin Blocking-Dialog muss die Zeile | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
attrs = DA_BLOCKING | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
im UI-Code gesetzt haben (ein späteres setzen im BASIC-Code ist möglich, aber meist nicht sinnvoll). Diese Zeile bewirkt folgendes:
OpenBlockingDialog()GEOS ist eine Multi-Thread-System. Selbst in einem R-BASIC Programm laufen zwei Threads (Prozesse) gleichzeitig: Der Code-Thread, der den von Ihnen geschrieben BASIC-Code ausführt und der UI-Thread, der die UI-Objekte bedient. Die Funktion OpenBlockingDialog() öffnet einen Blocking-Dialog. Der BASIC-Code Thread wird blockiert ("schlafen" gelegt, er verbraucht auch keine CPU-Zeit mehr), es läuft nur noch der UI-Thread. Dadurch kann der Nutzer die Objekte im Dialog bedienen (Listenelemente auswählen, Texte eingeben etc.). Um den Dialog zu beenden muss der Nutzer auf einen Schalter klicken, der einen interactionCommand-Wert gesetzt hat (vgl. Kapitel 4.6.4.2). Daraufhin kehrt OpenBlockingDialog() zurück und liefert den interactionCommand-Wert des Buttons, der betätigt wurde. Der BASIC-Code Thread wird fortgesetzt und kann den Wert auswerten. Achtung! OpenBlockingDialog liefert den Wert Null, wenn GEOS heruntergefahren wird, während ein Blocking-Dialog offen ist.
Schließen von Blocking-DialogenIn den meisten Fällen, insbesondere wenn Sie einen der Standard-Dialog-Typen gewählt haben (z.B. dialogType = DT_QUESTION, siehe Kapitel 4.6.6), schließt sich der Dialog automatisch. Sollte das nicht der Fall sein (z.B. weil Sie einen selbst definierten interactionCommand-Wert verwendet haben), müssen Sie den Dialog selbst schließen. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cmd = OpenBlockingDialog( SaveFilesDialog ) SaveFilesDialog.Close IF cmd = 1001 THEN ... | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Sie müssen, wie im Beispiel, den zurückgegebenen Wert "cmd" nicht überprüfen, da es erlaubt ist, die Close-Methode auch aufzurufen, wenn es gar nicht nötig wäre.
Beispiel 1: Ein einfacher Dialog. Er dient zur Verdeutlichung des Prinzips. An seiner Stelle könnte man auch den BASIC-Befehl MsgBox verwenden.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog InfoBox Caption$ = "Information" Children = InfoText attrs = DA_BLOCKING dialogType = DT_NOTIFICATION END Object Memo InfoText text$ = "Die Daten wurden erfolgreich gespeichert." readOnly = TRUE END Object | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Beispiel 2: Ein Dialog mit zwei Objekten.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog ErrorDialog
Caption$ = "Fehler !"
Children = ErrorValue, ErrorText
orientChildren = ORIENT_VERTICALLY
justifyChildren = J_CENTER
attrs = DA_BLOCKING
dialogType = DT_NOTIFICATION
END Object
Number ErrorValue
Caption$ = "Fehler Code:"
readOnly = TRUE
END Object
Memo ErrorText
Caption$ = "Beschreibung:"
justifyCaption = J_TOP
readOnly = TRUE
DrawInBox
END Object
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Der Dialog wird in einer SUB verwendet: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
SUB ShowError(err as integer) DIM retVal ErrorValue.value = err ErrorText.text$ = "Es fehlt eine notwendige Datei." retVal = OpenBlockingDialog( ErrorDialog ) END SUB | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Beispiel 3: Eine Dialog-Box mit 2 frei definierten Schaltern. Die Reply-Bar wird automatisch erzeugt, da die Buttons die Zeile "placeObject = REPLY_BAR" enthalten. UI-Code:
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Dialog NameDialog
Caption$ = "Name überprüfen"
Children = NameText, VornameText, CloseButton, OKButton
justifyChildren = J_CENTER_ON_CAPTION
attrs = DA_BLOCKING
END Object
InputLine NameText
Caption$ = "Name:"
end object
InputLine VornameText
Caption$ = "Vorname:"
end object
Button CloseButton
Caption$ = "Abbrechen"
placeObject = REPLY_BAR
interactionCommand = 1001
END Object
Button OKButton
Caption$ = "Ändern"
placeObject = REPLY_BAR
interactionCommand = 1002
END Object
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Im BASIC-Code der SUB "CheckName" wird der Dialog initialisiert, aufgerufen und dann ausgewertet: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
' globale Variablen
DIM gName$, gVorname$
<.. irgendwo im Code ..>
gName$ = "Würger"
gVorname$ = "Wilhelm"
SUB CheckName()
DIM cmd AS Word
NameText.text$ = gName$
VornameText.text$ = gVorname$
cmd = OpenBlockingDialog ( NameDialog )
NameDialog.Close
IF cmd = 1002 THEN
' Werte auslesen
gName$ = NameText.text$
gVorname$ = VornameText.text$
END IF
END SUB
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ergänzende Hinweise
^ |