Konfiguration von Xbase-Parts: Unterschied zwischen den Versionen
Georg (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Georg (Diskussion | Beiträge) |
||
Zeile 119: | Zeile 119: | ||
aSize := {300, 300} | aSize := {300, 300} | ||
cTitle := "Select Database For Operation" | cTitle := "Select Database For Operation" | ||
oDlgWin := XbpDialog():new( | oDlgWin := XbpDialog():new(AppDesktop(),; | ||
RootWindow():drawingarea, aPos, aSize, NIL, .F.) | RootWindow():drawingarea, aPos, aSize, NIL, .F.) | ||
oDlgWin:title := cTitle | oDlgWin:title := cTitle | ||
Zeile 176: | Zeile 176: | ||
Hierzu werden Position und Grösse des Dialogs festgelegt, und der Variablen cTitle die Überschrift für den Dialog zugewiesen. | Hierzu werden Position und Grösse des Dialogs festgelegt, und der Variablen cTitle die Überschrift für den Dialog zugewiesen. | ||
Wichtig ist, dass als Parent der Desktop verwendet wird. Standardmässig wird der Parent disabled, und damit auch der modale Dialog, wenn er als Child definiert ist. Dies trifft nicht zu, wenn der Desktop als Parent vorgegeben wird. | |||
Anschliessend wird der Instanzvariablen :title der String zugewiesen, der in cTitle hinterlegt ist. Man kann alternativ die Zuweisung auch ohne den Umweg über cTitle machen. | Anschliessend wird der Instanzvariablen :title der String zugewiesen, der in cTitle hinterlegt ist. Man kann alternativ die Zuweisung auch ohne den Umweg über cTitle machen. |
Aktuelle Version vom 30. April 2014, 11:24 Uhr
Was sind Xbase-Parts
Xbase-Parts sind überwiegend von Windows-Controls abgeleitete graphische Elemente, die dazu verwendet werden, graphische Benutzerschnittstellen zu erstellen.
Die unterstützten Fenstertypen sind ebenfalls als Xbase-Parts definiert.
Alle Xbase-Parts, die im Zusammenhang mit der XbpBrowse()-Klasse stehen, sind nicht direkt von "echten" Windows-Controls abgeleitet.
Der Lebenszyklus eines Xbase-Parts
Der Lebenszyklus eines Xbase-Parts ist grundsätzlich durch drei Methoden gekennzeichnet:
- :new()
- :create()
- :destroy()
Mit der Methode :new() wird ein neues Objekt erzeugt, das aber nicht immer zwingend auch direkt verwendbar ist, bevor nicht mittels :create() erforderliche Systemresourcen angefordert werden. Mit :destroy() kann ein Objekt gezielt zerstört werden. Die "Entsorgung" geschieht durch den Garbage Collector, wenn keine Verweise auf das Objekt mehr bestehen.
Thematisch gehört auch die Methode :configure() in diesen Kreis: sie wird verwendet, wenn das Objekt während seines Lebenszyklus geändert wird. Manche Änderungen werden erst wirksam, wenn das Objekt durch :configure() neu konfiguriert wird.
:new()
Jedes Xbase-Part "reagiert" auf die Methode :new() mit der Rückgabe einer Referenz auf ein neues Xbase-Part der eigenen Klasse:
oDlg := XbpDialog():new(<oParent>, <oOwner>, <aPos>, <aSize>,; <aPresParam>, <lVisible>)
Im Anschluss kann das neue Xbase-Part konfiguriert werden, um den gewünschten Anforderungen zu genügen.
Einige der Konfigurationsparameter können über eigene Instanzvariablen oder Methoden eingestellt (oder geändert) werden, während andere nur über das Präsentations-Parameter Array festgelegt werden können. Spätere Änderungen sind dann über eine Änderung der Präsentations-Parameter und einen Aufruf der Methode :configure() möglich.
Parameter
<oParent>
Der Parent ist das Objekt, in dessen Kontext das neue Objekt erstellt werden soll. Wird kein Parameter angegeben, ist das Objekt, das durch SetAppWindow() zurückgegeben wird, der Parent für das neue Objekt.
Diese Beziehung kann durch die Methode :setParent() abgefragt und auch verändert werden.
<oOwner>
Der Owner ist das Objekt, in dessen inhaltlichen Kontext das neue Objekt gestellt wird. Ein Beispiel ist ein XbStatic() vom Typ GroupBox, in der mehrere XbpRadioButton() angesiedelt werden. Durch den gleichen Owner ist geregelt, dass von all diesen XbpRadioButton() immer nur einer aus der Gruppe aktiv sein kann, die dem gleichen Owner zugeordnet sind.
Wird der Parameter Owner nicht angegeben, wird standardmässig der Parent ebenfalls als Owner verwendet.
<aPos>
aPos ist ein zwei-dimensionales Array, das standardmässig den linken, unteren Punkt angibt, an dem das Objekt platziert wird. Der erste Wert gibt die Entfernung in Pixel vom linken Rand des Parent an, während der zweite Wert die Entfernung in Pixel um unteren Rand des Parent angibt. Wird kein Array übergeben, ist der Unterlassungswert {0, 0}.
<aSize>
aSize ist ein zwei-dimensionales Array, das die Grösse des zu erzeugenden Xbase-Parts vorgibt. Der erste Wert gibt die Breite in Pixel an, der zweite Wert gibt die Höhe in Pixel an. Wird kein Array übergeben, wird das Xbase-Part mit einer Grösse von {0, 0} erzeugt.
<aPresParam>
<lVisible>
Der Parameter lVisible gibt an, ob das Xbase-Part nach seiner Erstellung sichtbar sein soll oder nicht. Die Sichtbarkeit hängt aber auch von der Sichtbarkeit des Parents ab: ist der Parent nicht sichtbar, ist auch das dem Parent zugeordnete Xbase-Part nicht sichtbar.
:create()
Wenn die Konfiguration abgeschlossen ist, wird das Xbase-Part "funktionsfähig" gemacht, indem Systemresourcen angefordert werden und (unter normalen Umständen) das Xbase-Part sichtbar gemacht wird.
Ab diesem Zeitpunkt können Änderungen an den Präsentations-Parametern nur über
oXbp:setPresParam(aPP) oXbp:configure()
geändert werden, d.h. das Xbase-Part muss explizit neu konfiguriert werden, damit die Änderungen auch wirksam werden.
:destroy()
Diese Methode wird wohl selten explizit ausgeführt. Sie wird jedoch rekursiv durchgeführt, wenn z.B. ein XbpDialog() mittels :destroy() zerstört wird. In diesem Fall wird bei allen abhängigen Xbase-Parts die Methode :destroy() ausgeführt, sofern sie nicht noch in einem anderen Kontext vorhanden sind (z.B. abweichender Owner).
Auswahl von Konfigurations-Variablen:
Falls verfügbar, zeigt diese Variable einen beschreibenden/ergänzenden Text am Xbase-Part an.
:tabStop
Diese Instanz-Variable legt fest, ob das Xbase-Part unter Verwendung der Tab-Taste angesprungen werden kann (Standard-Verhalten unter Windows). Entscheidend für die Reihenfolge, in der die Xbase-Parts angesprungen werden, ist die zeitliche Reihenfolge, in der :tabStop gesetzt wurde.
:cargo
Diese Instanz-Variable wird von Xbase++ nicht verwendet, sondern räumt dem Programmierer die Möglichkeit ein, bestimmte Information in einem Xbase-Part zu hinterlegen, ohne eine eigenen Klasse abzuleiten und darin weitere Felder zu definieren. Verwendung von :cargo auf eigene Gefahr.
Beispiele
Beispiel 1 - Auswahl aus einer Liste über einen modalen Dialog
Dialoge unter Windows sollten so gestaltet werden, dass sie nicht modal sind. Unter einem modalen Dialog versteht man eine Abfrage, die ein anderweitiges Arbeit mit dem Programm so lange blockiert, bis die Abfrage beantwortet wurde.
Das Beispiel fragt zu Programm-Start ab, welche SQL-Datenbank für den Programm-Lauf verwendet werden soll. Unterstellt wird die Verwendung der AppSys() aus dem AppSys.prg Artikel
#INCLUDE "Xbp.CH" Function Main() Local aPos, aSize, aList Local cTitle, cDataBase Local nI, nResult Local oDlg, oDlgWin, oXbp, oList // Schritt 1 - Definition der Auswahlliste aList := {"Produktion", "Test 1", "Test 2", "Remote Test"} // Schritt 2 - Erstellen des Dialogs aPos := {100, 100} aSize := {300, 300} cTitle := "Select Database For Operation" oDlgWin := XbpDialog():new(AppDesktop(),; RootWindow():drawingarea, aPos, aSize, NIL, .F.) oDlgWin:title := cTitle oDlgWin:create() // Schritt 3 - Bereitstellen einer Variablen für die :drawingArea oDlg := oDlgWin:drawingArea // Schritt 4 - Erzeugen einer XBpListbox() aPos := {10, 50} aSize := {260, 200} oList := XbpListBox():new(oDlg, oDlg, aPos, aSize) oList:tabStop := .T. oList:create() // Schritt 5 - Füllen der XbpListBox() mit den Daten der Auswahlliste FOR nI := 1 TO Len(aList) oList:addItem(aList[nI]) NEXT // Schritt 6 - Erstellen eines OK-Buttons aPos := {10, 10} aSize := {120, 30} oXbp := XbpPushButton():new(oDlg, oDlg, aPos, aSize) oXbp:tabStop := .T. oXbp:caption := "OK" oXbp:default := .T. oXbp:create() // Schritt 7 - Erstellen eines Cancel-Buttons aPos := {160, 10} oXbp := XbpPushButton():new(oDlg, oDlg, aPos, aSize) oXbp:tabStop := .T. oXbp:caption := "Cancel" oXbp:cancel := .T. oXbp:create() // Schritt 8 - Vorbereiten der Anzeige des XbpDialog() CenterControl(oDlgWin) // Schritt 9 - modale Anzeige des Dialogs nResult := oDlgWin:showModal() // Schritt 10 - verstecken und zerstören des Dialogs oDlgWin:hide() oDlgWin:destroy() // Schritt 11 - Abfrage der Auswahl IF nResult = XBP_MRESULT_OK cDatabase := oList:getItem(oList:getData()[1]) ELSE cDatabase := "" ENDIF ConfirmBox(, "Ausgewählt wurde Datenbank " + cDatabase, "Ergebnis",; XBPMB_OK, XBPMB_INFORMATION) RETURN(.T.)
Schritt 1 - Definition der Auswahlliste
Der Variablen aList wird für unser Beispiel eine Liste von zur Verfügung stehenden Datenbanken zugewiesen.
Schritt 2 - Erstellen des Dialogs
Hierzu werden Position und Grösse des Dialogs festgelegt, und der Variablen cTitle die Überschrift für den Dialog zugewiesen.
Wichtig ist, dass als Parent der Desktop verwendet wird. Standardmässig wird der Parent disabled, und damit auch der modale Dialog, wenn er als Child definiert ist. Dies trifft nicht zu, wenn der Desktop als Parent vorgegeben wird.
Anschliessend wird der Instanzvariablen :title der String zugewiesen, der in cTitle hinterlegt ist. Man kann alternativ die Zuweisung auch ohne den Umweg über cTitle machen.
Danach wird der Dialog erzeugt, indem Systemresourcen angefordert werden. Der Dialog selbst ist aber noch "unsichtbar".
Schritt 3 - Bereitstellen einer Variablen für die :drawingArea
Ein Fenster ist erst einmal ein rechtwinkliger Bereich. Ein XbpDialog() umfasst standardmässig (!) sowie die sogenannte Titlebar mit Systemmenü, Überschrift, sowie den Buttons zum Minimieren, Maximieren und Schliessen. Ebenfalls zur Grundausstattung gehört der Rahmen um einen solchen Dialog, und eventuell auch eine Menüleiste.
Der XbpDialog() besitzt einen als :drawingArea bezeichneten Bereich, der innerhalb (!) der gerade beschriebenen Elemente liegt und als "Zeichnungsfläche" für Xbase-Parts verwendet werden kann (soll).
Nach dieser Zuweisung sind oDlgWin:drawingArea und oDlg identisch, sie zeigen beide auf den gleichen Bereich. Die Verwendung der Variablen oDlg erlaubt es, mit weniger Schreibarbeit auszukommen.
Schritt 4 - Erzeugen einer XBpListbox()
Die XbpListBox() soll im oberen Bereich des Fensters angezeigt werden, entsprechend werden die Variablen aPos und aSize belegt. Da alle Elemente in diesem Dialog mit der Tab-Taste anspringbar sein sollen, muss bei jedem Xbase-Part die Instanzvariable :tabStop mit TRUE belegt werden.
Schritt 5 - Füllen der XbpListBox() mit den Daten der Auswahlliste
Erst nach dem Ausführen von :create() können nun Werte in die XbpListBox() eingetragen werden. Dies geschieht durch die Methode :addItem(). Die hinzugefügten Einträge werden in der Reihenfolge angezeigt, in der sie mittels :addItem() eingetragen wurden!
Schritt 6 - Erstellen eines OK-Buttons
Da wir mit einem modalen Dialog arbeiten wollen, gibt es bestimmte Bedingungen, die erfüllt werden müssen. Dazu gehört, dass es ein XbpPushButton()-Element geben muss, dessen Instanzvariable :default auf TRUE gesetzt sein muss (dies ist die Standard-Auswahl). Wie vorher bemerkt, soll der Anwender zwischen den Elementen mit der Tab-Taste springen können, daher wird auch hier :tabStop auf TRUE gesetzt. Als Caption wird "OK" vorgegeben.
Schritt 7 - Erstellen eines Cancel-Buttons
Um eine Auswahl zu bieten, muss auch ein Cancel-Button vorhanden sein. Analog zum OK- oder Default-Button gibt es auch hier eine Instanz-Variable, :cancel, die mit TRUE belegt sein muss, um dem XbpDialog() zu signalisieren, dass dieser XbpPushButton() zum Beenden des modalen Dialogs verwendet werden kann.
Schritt 8 - Vorbereiten der Anzeige des XbpDialog()
Mittels CenterControl(oDlgWin) wird der Dialog auf dem Bildschirm zentriert.
Schritt 9 - modale Anzeige des Dialogs
Die Methode :showModal() ist eine Neuerung, die einen Dialog ohne Event-Loop anzeigt. Der Event-Loop wird intern realisiert, und die Methode gibt zurück, ob OK oder Cancel ausgewählt wurde. Da wir hier gleichzeitig aber auch eine weitere Auswahl vornehmen wollen, müssen wir noch auf den Status von oList zugreifen:
Schritt 10 - verstecken und zerstören des Dialogs
Als erstes werden wir - in diesem Beispiel - den Dialog verstecken und zerstören. Wir haben ja noch eine Referenz auf die XbpListBox() in der Variablen oList.
Schritt 11 - Abfrage der Auswahl
Wenn der Dialog mit OK beendet wurde, lesen wir den ausgewählten Eintrag der XbpListBox() aus. Dies geschieht über die Methode :getData().
Eine Bemerkung zur Verwendung der Variablen
Es fällt vielleicht auf, dass die zwei XbpPushButton() unter Verwendung der gleichen Variablen, oXbp, erstellt werden.
Unter Clipper war es so, dass mit der zweiten Zuweisung die erste zerstört worden wäre, aber wir sind nicht mehr unter DOS, sondern unter Windows.
Bei der Erzeugung der XbpPushButton() wird dieser im Kontext des <oParent> erstellt. Man kann sich das - ganz stark vereinfacht - als eine Art AAdd(oParent, oXbp) vorstellen. Wenn die Variable oXbp verändert wird, bleibt der Eintrag in dem Array oParent unverändert.
Vielleicht kommt jetzt der Einwand: "Wie greife ich dann auf das oXbp zu, das auf den XbpPushButton() verweist?" Die Antwort ist einfach: "Gar nicht."
Ein direkter Zugriff auf die XbpPushButton() ist (eigentlich) nicht mehr erforderlich. Wenn etwas mit der XbpListBox() passiert, auf das unser Programm reagieren muss, dann kommt der Event-Loop ins Spiel: hier erhalten wir dann ein entsprechendes Event (mit oder ohne belegte Message Parameter) und eine Referenz auf die XbpListBox().
In diesem Beispiel wird die XbpListBox() mit einer eigenen Variablen erzeugt, oList - der Grund ist einfach: nach dem :showModal() wollen wir auf die XbpListBox() zugreifen und Daten auslesen. Darum müssen wir die Referenz auf dieses Xbase-Part "festhalten".
Ergebnis
Abbruch.
Der Zugriff auf oList funktioniert nicht so, wie man es erwarten würde. Obwohl ein Element ausgewählt und dann OK geklickt wurde, liefert
oList:getData()
ein leeres Array als Ergebnis zurück. Im Debugger bringt auch ein
oList:getItem(1)
als Ergebnis einen leeren String.
Mit der Anweisung oDlgWin:destroy() wurden auch alle abhängigen Xbase-Parts "zerstört". Dabei handelt es sich um
- XbpListBox
- XbpPushButton (OK)
- XbpPushButton (Cancel)
Da das XbpListBox-Element noch durch oList referenziert wird, wird die gesamte Kette eben nicht entsorgt, sondern oDlgWin, oDlg und oList bleiben "vorhanden", aber in einem Zustand, der "nicht gebrauchsfähig" ist.
Beispiel 1a - Auswahl aus einer Liste über einen modalen Dialog
Für dieses Beispiel ändern wir die Reihenfolge der letzten Anweisungen:
// Schritt 9 - modale Anzeige des Dialogs nResult := oDlgWin:showModal() // Schritt 10 - Abfrage der Auswahl IF nResult = XBP_MRESULT_OK cDatabase := oList:getItem(oList:getData()[1]) ELSE cDatabase := "" ENDIF ConfirmBox(, "Ausgewählt wurde Datenbank " + cDatabase, "Ergebnis",; XBPMB_OK, XBPMB_INFORMATION) // Schritt 11 - verstecken und zerstören des Dialogs oDlgWin:hide() oDlgWin:destroy()
Wenn das :destroy() nach der Abfrage ausgeführt wird, ist die Auswirkung auf unser Problem egal, da wir bereits die erforderliche Information abgefragt haben.
Beispiel 2 - Auswahl aus einer Liste über einen modalen Dialog mit mehr Komfort
Normalerweise erwartet ein Anwender, dass man (eventuell) durch einen Doppelklick auf den Eintrag in der ListBox diesen auswählen kann. Unser Dialog erlaubt das aber nicht, daher verwenden wir den Callback-Slot :itemSelected des XbpListBox() Objektes, um bei Auswahl (= Doppelklick) eine bestimmte Aktion ausführen zu lassen.
Function Main() Local aPos, aSize, aList Local cTitle, cDataBase Local nI, nResult Local oDlg, oDlgWin, oXbp, oList // Schritt 1 - Definition der Auswahlliste aList := {"Produktion", "Test 1", "Test 2", "Remote Test"} // Schritt 2 - Erstellen des Dialogs aPos := {100, 100} aSize := {300, 300} cTitle := "Select Database For Operation" oDlgWin := XbpDialog():new(RootWindow():drawingArea,; RootWindow():drawingarea, aPos, aSize, NIL, .F.) oDlgWin:title := cTitle oDlgWin:create() // Schritt 3 - Bereitstellen einer Variablen für die :drawingArea oDlg := oDlgWin:drawingArea // Schritt 4 - Erzeugen einer XBpListbox() aPos := {10, 50} aSize := {260, 200} oList := XbpListBox():new(oDlg, oDlg, aPos, aSize) oList:tabStop := .T. oList:create() // Schritt 5 - Füllen der XbpListBox() mit den Daten der Auswahlliste FOR nI := 1 TO Len(aList) oList:addItem(aList[nI]) NEXT // Schritt 6 - Erstellen eines OK-Buttons aPos := {10, 10} aSize := {120, 30} oXbp := XbpPushButton():new(oDlg, oDlg, aPos, aSize) oXbp:tabStop := .T. oXbp:caption := "OK" oXbp:default := .T. oXbp:create() // Schritt 6a - Verwendung von :itemSelected oList:itemSelected := {|| oXbp:activate(NIL, NIL, oXbp)} // Schritt 7 - Erstellen eines Cancel-Buttons aPos := {160, 10} oXbp := XbpPushButton():new(oDlg, oDlg, aPos, aSize) oXbp:tabStop := .T. oXbp:caption := "Cancel" oXbp:cancel := .T. oXbp:create() // Schritt 8 - Vorbereiten der Anzeige des XbpDialog() CenterControl(oDlgWin) // Schritt 9 - modale Anzeige des Dialogs nResult := oDlgWin:showModal() // Schritt 10 - Abfrage der Auswahl IF nResult = XBP_MRESULT_OK cDatabase := oList:getItem(oList:getData()[1]) ELSE cDatabase := "" ENDIF // Schritt 11 - verstecken und zerstören des Dialogs oDlgWin:hide() oDlgWin:destroy() ConfirmBox(, "Ausgewählt wurde Datenbank " + cDatabase, "Ergebnis",; XBPMB_OK, XBPMB_INFORMATION) RETURN(.T.)
Schritt 6a - Verwendung von :itemSelected
Wir legen fest, dass bei der Auswahl eines ListBox-Elements die :activate-Methode von oXbp ausgeführt werden soll.
Schritt 10 - Abfrage der Auswahl
Erstaunlicherweise erhalten wir jetzt immer XBP_MRESULT_CANCEL?
In dem Codeblock, den wir in Schritt 6a definiert haben, wird auf oXbp verwiesen. Zum Zeitpunkt der Codeblock-Definition (!) deutet oXbp auf den OK-Button. Danach wird oXbp verwendet, um den Cancel-Button zu erstellen. Darum verweist oXbp (auch im Codeblock) auf den Cancel-Button.
Wir benötigen also eine weitere Variable, um den Cancel-Button erstellen zu können:
LOCAL oXbpC ... aPos := {160, 10} oXbpC := XbpPushButton():new(oDlg, oDlg, aPos, aSize) oXbpC:tabStop := .T. oXbpC:caption := "Cancel" oXbpC:cancel := .T. oXbpC:create()
Und auf einmal funktioniert es, wie gewünscht:
Ein Doppelklick auf den gewünschten Eintrag in der ListBox löst automatisch auch einen virtuellen Klick auf den OK-Button aus.