VOICE Homepage: http://de.os2voice.org |
März 2003
[Inhaltsverzeichnis]
|
Von Thomas Klein © März 2003 |
Entschuldigen Sie bitte das Fehlen der Fortsetzung in der Ausgabe vom letzten
Monat, aber nun geht's weiter: Mit dem container control.
Wie ich bereits anklingen ließ, ist dieses Steuerlement ein Sonderfall.
Es beherbergt eine Fülle an GUI-Komfort und ist unglaublich vielseitig
und nützlich, vom visuellen Standpunkt her sehr "anziehend" und bietet
dem Endbenutzer sehr komfortable Funktionalität.
Leider fordern all diese Vorteile ihren Preis, nämlich ein entsprechend
hohes Volumen an Informationen, Daten und Steuerung, damit es das tut, was man
möchte. Einen guten Einstieg in diese Fülle von Methoden, Ereignissen
und Befehlen zu finden, die unterstützt bzw. benötigt werden, ist
auf den ersten Blick tatsächllich nicht gerade leicht. Ich möchte
daher versuchen, diesen Artikel als eine Art strukturierten Führer durch
das Online-Hilfematerial anzulegen. Außerdem ist zu diesem Artikel eine
Beispielanwendung über die VOICE-Website verfügbar (s. Quellenhinweis
am Artikelende), die Ihnen einen ersten Blick hinter die Kulissen gestattet,
wenn es um das Arbeiten mit container controls geht.
(Diese Beispielanwendung diente auch zur Erstellung der meisten Bildschirmabbildungen
in diesem Artikel.)
[Die auf der VOICE-Webseite zum Herunterladen verfügbaren Archive enthalten die Beispielanwendung
in einer ZIP-Datei. - Anm.d.Red.]
Im Prinzip handelt es sich beim container control - aus der Sicht des Endanwenders
- um eine Art "Ordner". Enthaltene Objekte können (unter anderem) als
Symbole, mehrspaltig oder auch als hierarchische "Baum"-Struktur angezeigt
werden. Das besondere daran ist, daß Sie (der Entwickler) totale Kontrolle
über alle Eigenschaften haben: Was wird angezeigt, wie läuft
es ab und wie reagiert es auf Benutzeraktionen. Der Haken an der Sache ist
andererseits, daß Sie sich mit diesen Sachen leider beschäftigen
müssen, da das container control
absolut nichts von alleine macht. ;)
Es besitzt keinen Automatismus mit dem man z.B. lediglich auf ein Verzeichnis
verweisen kann und schon läuft's - nö. In solchen Fällen wäre
der Entwickler (also Sie) dazu gezwungen, zunächst alle Dateien und
Unterverzeichnisse des Verzeichnisses "manuell" zu ermitteln und damit den
container entsprechend zu füllen.
Um Ihnen einen kurzen Überblick darüber zu geben, was man mit dem
container control anstellen kann, nehmen wir als Beispiel ein fiktives Adreßbuchprogramm
(oder eine "Buddylist", wenn Sie so wollen). Die enthaltenen Einträge
bestehen aus dem Namen, einem zugewiesenen Bitmap und zusätzlichen Daten
wie Telefonnummer und E-Mail-Adresse. Die unterschiedlichen Ansichten ("view"-Typen),
die mit dem container control zur Verfügung stehen, sind...
TEXT - Einträge werden in einer einzelnen Spalte angezeigt, die nur den jeweiligen
Namen enthält.
COLUMN - Wie TEXT, aber mehrspaltig.
NAME - Einspaltige Darstellung, die sich aus dem Namen und - links davon - dem Bitmap
des Eintrags zusammensetzt.
FLOWEDNAME - Wie NAME, aber mehrspaltig.
BITMAP - Wie die Symbolansicht eines WPS-Ordners: Die Einträge bestehen
aus dem Bitmap und dem darunter zentriert angezeigten Namen. Zur Anordnung
dient ein gitterähnliches Verfahren.
DETAIL - Wie die Detailansicht eines WPS-Ordners: Zu jedem Eintrag existiert
eine Zeile in einer tabellenähnlichen Struktur.
Diese kann aus einer beliebigen Anzahl von Spalten bestehen, die sich wahlweise
in einem einzelnen Fenster oder in einer zweiteiligen Darstellung befinden,
bei der eine vertikale, verschiebbare Trennlinie zur Größenänderung
verwendet wird.
OUTLINE - Die Einträge haben den gleichen Umfang wie in NAME, jedoch wird zusätzlich
eine Eltern/Kind-Beziehung zwischen ihnen angezeigt, indem untergeordnete
Einträge jeweils nach rechts eingerückt werden. Übergeordnete
Einträge erhalten zusätzlich ein Symbol, mit dem die Ansicht um
die untergeordneten Einträge (soweit vorhanden) erweitert oder reduziert
werden kann.
HIERARCHY - Wie die Strukturanzeige in WPS-Ordnern und eigentlich wie OUTLINE, jedoch
erweitert um Linien, die vom übergeordneten Eintrag zu dessen untergeordneten
Einträgen führen.
Um Ihnen einen kleinen "Appetitanreger" für die Arbeit mit containern
zu geben: Von einer der oben abgebildeten Ansichten zu einer anderen zu
wechseln (unter Beibehaltung aller Einträge) erfordert nur eine Zeile Programmcode
- um genau zu sein: Eine einzige Anweisung... ;) Natürlich erfordert
das ein wenig Arbeit im Vorfeld, zum Beispiel das eigentliche "Füllen"
des containers.
Der Arbeitsaufwand ergibt sich dabei in erster Linie aus der/den Ansicht(en),
die Sie dem Endbenutzer zur Verfügung stellen möchten: Eine "einfache"
Darstellung wie BITMAP erfordert lediglich eine einzelne und relativ simple
Anweisung, um dem container ein Objekt hinzuzufügen... während -
wie Sie sich sicherlich vortsllen können - die Darstellungsform DETAIL
einiges mehr an Daten erfodert, weil damit ja auch eine größere
Menge an Daten dargestellt werden kann und das Anzeigeformat weitaus umfangreicher
ist. Dasselbe gilt auch für die beiden Strukturansichtsformen HIERARCHY und
OUTLINE. Obwohl hierbei ebenfalls
nur Bitmap und Text eines Eintrags angezeigt werden, benötigt man zusätzliche
Informationen um die Zuordnungen zwischen "Eltern" und "Kindern" darstellen
zu können. Wenn man aber erst einmal alle benötigten Daten bereitgestellt
hat, macht das Arbeiten mit Container-Elementen wirklich Spaß.
Aber bevor wir uns anschauen, wie man Einträge in einem container anlegt,
sollten wir ein paar Dinge klären...
Bevor Sie ein Container-Element auf Ihren Dialog ziehen, sollten Sie sich darüber
im klaren sein, welches Maß an Arbeit auf Sie als Entwickler zukommt, je nach
dem, wie der Endbenutzer es verwendet... um das etwas zu verbildlichen: Stellen
Sie sich eine Adreßverwaltung auf Basis eines containers vor, wie Sie oben
abgebildet ist. Was passiert zum Beispiel, wenn der Anwender einen neuen
Eintrag anlegen will? Gut, das wäre relativ einfach: Sie zeigen einen
neuen Dialog an, in dem die Daten des neuen Eintrags erfaßt werden und fügen
ihn nach Bestätigung in den container ein - so weit, so gut. Aber...
wenn der Benutzer jetzt einen Eintrag löschen will? Das könnte
zum Beispiel geschehen, indem ein Kontextmenü benutzt wird, die Taste
<Entf> gedrückt wurde oder eine andere Aktion durchgeführt wird,
die Sie dem Endanwender anbieten. Zusätzlich kann der Anwender durch
Anklicken eines Spaltenwerts mit gedrückter Taste <Alt> diesen ändern
- sofern die Spalte nicht als "schreibgeschützt" definiert wurde (dazu
kommen wir später). Das Container-Element kümmert sich zwar selbsttätig
um die notwendigen Schritte (Umschaltung von Anzeige- auf Eingabefeld und
Übernahme der Eingabe), aber ungeachtet dessen, was auf der Benutzeroberfläche
passiert, sind Sie dafür zuständig, die Eingaben und Änderungen
des Benutzers in der Datei bzw. Datenbank zu speichern...
Um es zusammenzufassen: Sie sollten den Funktionsumfang des Steuerelements
"container" kennen, die dem Benutzer damit zur Verfügung stehenden Möglichkeiten
und daraus eine Art Benutzerführung für Ihren Anwendungszweck entwickeln:
Wie soll der Anwender Einträge hinzufügen, löschen oder ändern
können? Okay - fangen wir einmal mit den Ereignissen und Methoden (Funktionen,
Befehlen) eines Containerelements an:
Die einzelnen nachfolgenden Ereignisse treten jeweils für das Container-Element
als solches auf. Da ein container jedoch - wie der Name es vermuten
läßt - zur Aufnahme mehrerer Objekte dient, ist es manchmal nötig
zu wissen, welches der enthaltenen Objekte eigentlich betroffen ist. Diese
Informationen sind zwar verfügbar, jedoch muß dafür eine zusätzliche
Funktion namens EventData
verwendet werden. Die Syntax für
EventData
lautet
call EventData (stamm)
wobei stamm
der Name einer Stammvariable ("stem variable") ist.
Wenn die Informationen von EventData
beispielsweise in eine Stammvariable
namens "meinstamm" abgelegt werden sollen, muß die Anweisung dafür lauten:
call EventData ("meinstamm")
Ja - einschließlich der Anführungszeichen!
Wenn Sie bisher nichts von Stammvariablen gehört haben, werden Sie sich
bestimmt fragen, um was es dabei eigentlich geht. Ich versuch's 'mal so: Eine
Stammvariable ist im Prinzip eine eindimensionale Tabelle (quasi eine Liste)
von Variablen, die alle den gleichen Namen haben und sich nur durch einen
zusätzlichen Index (wie eine laufende Nummer) unterscheiden. Die Schreibweise
für die Verwendung solcher Stammariablen ist dabei relativ einfach.
Am Anfang steht der "Stamm"-Name der Variable, dann folgt ein Punkt und
danach der Index des jeweiligen Elements - entweder direkt als Ziffer oder
durch Angabe einer Variable, die den Wert für den Index enthält.
Wenn Sie eine Stammvariable namens "meinstamm" anlegen wollen, die drei Einträge
enthält, würden Sie das so codieren:
meinstamm.1 = "erster"
meinstamm.2 = "zweiter"
meinstamm.3 = "dritter"
Danach verwenden Sie den Eintrag Nummer 0 (NULL), um darin die Größe (Anzahl der Einträge) der Stammvariable abzulegen:
meinstamm.0 = 3
Wenn Sie eigene Stammvariablen verwenden (also "manuell" anlegen), dann ist
diese letzte Anweisung eigentlich nicht erforderlich, jedoch handelt es sich
dabei um ein gängiges Verfahren in REXX - auch die Systemfunktionen wie
z.B. das Suchen von Dateien auf der Platte verwenden dieses Verfahren, um
zu kennzeichnen, wie viele Einträge zu einer Stammvariable vorhanden sind.
Auf diese Weise kann der Programmierer mittels Eintrag Nummer 0 nach dem
Aufruf der Funktion ermitteln, aus wie vielen Einträgen die Stammvariable
besteht. Das ist unbedingt nötig, um beispielsweise in einer Schleife
alle Einträge der Stammvariable zu verarbeiten. Na, egal - das sollte
für's erste jedenfalls reichen, damit Sie einen ersten Eindruck von Stammvariablen
gewinnen und wir mit der Funktion EventData
fortfahren köennen
(wir kommen in einem späteren Teil der Serie nochmals auf die Stammvariablen
zurück).
Wohlan, denn: Lassen Sie uns die Syntax von EventData
nochmals
anschauen... in der Online-Hilfe von DrDialog steht dazu:
EventData( [stamm] )
Liefert die Liste von Einträgen, auf die sich das Ereignis bezieht, in die durch
[stamm]
angegebene Stammvariable. Wird[stamm]
nicht angegeben, wird dafürEVENTDATA
verwendet.Die Anzahl der betroffenen Einträge wird in
stamm.0
abgelegt.stamm.1
bisstamm.n
enthält jeweils ein vom aktuellen Ereignis betroffenes Element. Für eine detailierte Beschreibung der zu einem bestimmten Ereignis für ein bestimmtes Steuerelement zurückgegebenen Daten beachten Sie den Abschnitt "Steuerelemente".
[..]
Daraus ergibt sich...
Erstens: Es muß keine Stammvariable angegebgen werden. In diesem Fall wird
vom System ein "eingebauter" Vorgabewert benutzt, der eine Stammvariable namens
"EVENTDATA" für diesen Zweck anlegt und verwendet.
Zweitens: Wie wir bereits weiter oben gesehen haben, wird die Anzahl der zurückgegebenen
Einträge im Stammvariableneintrag Nummer 0 abgelegt.
Bevor wir jetzt endlich zu den Ereignissen kommen, müssen wir noch eine
Kleinigkeit klären:
Während die Einträge in einer Liste oder Combobox durch einen simplen,
fortlaufenden Index identifiziert werden (1 = erster, 2 = zweiter usw.),
werden die Einträge eines containers gänzlich anders verwaltet.
Jeder Eintrag besitzt eine eindeutige "ID" ("Schlüssel", wenn Sie so
wollen), der zum Zeitpunkt des Anlegens des Eintrags vom System generiert
wird. Diese IDs beginnen nicht bei 1, noch sind sie fortlaufend oder gar sortiert.
Verwenden Sie sie keinesfalls, um irgend etwas zu berechnen. Sie dienen ausschließlich
dazu, Objekte (Einträge) innerhalb des containers zu identifizieren
und haben "außerhalb" des containers keinerlei Bedeutung. Außerdem
werden Sie feststellen, daß für dasselbe Objekt eines container
bei einem nächsten Programmlauf eine vollkommen andere ID generiert
wird, wodurch die IDs also auch beispielsweise für direkte Datensatzreferenzen
oder andere "statische" Zwecke gänzlich unbrauchbar sind... wie man
damit umgeht, besprechen wir später. Dieser Aschnitt sollte Ihnen nur
klar machen, was es mit den IDs auf sich hat und Sie einen besseren Einstieg
in die folgende Thematik finden lassen. Beachten Sie, daß die folgenden Beispiele
darauf basieren, daß keine eigene Stammvariable verwendet wird, und
daher die vom System bereitgestellte Stammvariable "EVENTDATA" verwendet
wird.
für das Container-Ereignis... | liefert CALL EVENTDATA Ihnen folgende Informationen... |
||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Changed Der Anwender hat ein Feld eines Containereintrags (Objekts) geändert |
|
||||||||||||
Enter Der Anwender hat entweder die Eingabetaste gedrückt oder innerhalb des Containers einen Doppelklick mit der Maus durchgeführt. |
|
||||||||||||
Select Der "Zustand" eines Containerelements (Auswahl, Markierung, Cursor) hat sich geändert. |
|
||||||||||||
ShowMenu Der Anwender hat (durch Klicken von Maustaste 2 im container) ein Kontextmenü angefordert. |
|
Natürlich gibt es noch mehr Ereignisse für einen Container, aber diese sind eher "einfacher" Natur und erfordern kein zusätzliches Ermitteln weiterer Information:
Init |
Der Container wird initialisiert (während des OPEN-Ereignis des Dialogs). |
Scroll |
Der Inhalt des Conatiners wurde geblättert. |
GetFocus |
Der Container hat den "Fokus" erhalten (= ist das "aktive" Element im Dialog geworden). |
LoseFocus |
Der Container hat den "Fokus" verloren (= ein anderes Element im Dialog ist jetzt das "aktive"). |
Drop |
Ein Objekt wurde auf den Container gezogen und abgelegt. Dies erfodert die Verwendung von EVENTDATA in Zusammenhang mit dem DROP -Ereignis,
um nähere Informationen zu erhalten - wir besprechen Drag-and-Drop-Ereignisse
(Ziehen und Übergeben) in einem späteren Artikel... |
Ein letztes Beispiel zur Verwendung des EVENTDATA
-Krempels: Wenn Sie beispielsweise
auf ein CHANGED
-Ereignis reagieren wollen, müssen Sie:
CHANGED
des containers im code editor auswählen und dortz.B. folgendes codieren:
CALL EVENTDATA say "ID des geänderten Eintrags ist" EVENTDATA.1 say "am Eintrag wurde geändert" EVENTDATA.2
oder, wenn Sie lieber eine eigene Stammvariable verwenden möchten:
CALL EVENTDATA("meinstamm") say "ID des geänderten Eintrags ist" meinstamm.1 say "am Eintrag wurde geändert" meinstamm.2
Keine Panik - wir kommen noch darauf zu sprechen, wie es in solchen Fällen
eigentlich weitergeht...
Okay, super. Jetzt wo wir wissen, was es mit den Ereignissen eines Containers
auf sich hat... wie zum Teufel kriegen wir denn jetzt Einträge hinein?
Kommen wir also zu den Methoden (oder "Funktionen" wenn Sie's so wollen) -
beachten Sie, daß es noch weitere Methoden gibt, die für einen
Container verwendet werden können, es sich bei diesen aber um gängige
Methoden handelt, die auch bei anderen Steuerelementen verwendet werden. Und
da wir diese Methoden bereits besprochen haben, konzentrieren wir uns nun
nur auf die Container-spezifischen Methoden:
Fangen wir doch mit der Funktion VIEW
an. Damit wird die Darstellung des Containers
ausgewählt. (Erinnern Sie sich noch an die Abbildungen zu Beginn des Artikels?
Genau dafür wird VIEW verwendet.) Um beispielsweise für einen Container
namens "meincontainer" die einspaltige "nur-Namen"-Anzeige auszuwählen,
müßten Sie codieren
call meincontainer.view "Text"
Beachten Sie, daß es für den view-Typ (die Darstellungsform) auch genügt, nur den ersten Buchstaben anzugeben - so würde also die Anweisung
call meincontainer.view "T"
dasselbe tun, wie die Anweisung vorher. Das Auswählen der anfänglichen
Darstellung erledigt man idealerweise im Ereignis INIT
für
den Container. In diesem Fall braucht man dann auch - wie wir noch wissen
- nicht den Namen des controls anzugeben, da man sich innerhalb des INIT
event handlers befindet und somit "programmierungsmäßig" im Kontext
des controls selbst. Man kann das ganze also abkürzen in
call view "T"
Nun, da Sie gesehen haben, wie einfach die Funktion VIEW
sein
kann, werfen wir doch 'mal einen Blick auf deren vollständige Syntax:
oldview = container.view([type], [title],
[bitmapwidth, bitmapheight], [expandbitmap, collapsebitmap])
Uff! Lassen Sie uns zunächst einmal wieder darauf besinnen, daß solche Funktionen verwendet werden können, um Werte zu setzen, abzufragen oder beides gleichzeitig. Man könnte die "Funktionsschreibweise" (die einen Wert zurückgibt) einsetzen, um die aktuellen Darstellungsdaten eines Containers zu ermitteln. Aber mal ehrlich - das wollen wir doch im Moment gar nicht. Wir wollen nichts abfragen, also nehmen wir einfach
call container.view [type], [title], [bitmapwidth, bitmapheight], [expandbitmap, collapsebitmap]
Wie wir bereits gesehen haben, handelt es sich beim Parameter [type] um die
Darstellungsform (die Ansicht) des Containers ("text" im obigen Beispiel).
[title] bestimmt den Titel des Containers ("buddylist" in den Bildschirmabbildungen).
Das ist eine Zeichenkette, die sich als Überschrift über dem eigentlichen
Inhalt des Containers - den Einträgen also - befindet. Der Titel kann
bestimmte Zeichen enthalten, mit denen das Format der Überschrift beeinflußt
werden kann:
< | Titel wird linksbündig dargestellt. |
> | Titel wird rechtsbündig dargestellt. |
| ("pipe"-Symbol, AltGr + "<") | Der Titel wird zentriert (ist übrigens auch der Vorgabewert). |
_ (Unterstrich) | Zwischen dem Titel und dem Inhaltsbereich des Containers wird eine Trennlinie gestellt. |
; | Beendet die Sequenz der Formatsteuerung, alle folgenden
Zeichen werden für die eigentliche Überschrift verwendet. Das ist optional - wird keine Semikolon verwendet, beginnt die Überschrift mit dem ersten Zeichen, das nicht als Formatzeichen erkannt wird (also jedes Zeichen, das nicht in dieser Liste enthalten ist). |
Werden keine Formatzeichen im Titel verwendet, so wird die Überschrift
zentriert und ohne Trennlinie dargestellt. Wenn Sie "Mein erster Container"
als Überschrift in Ihrem Container sehen wollen, dazu linksbündig
und mit einer Trennlinie, muß der Wert für den Parameter [title]
in
Ihrem VIEW-Befehl lauten:
"<_Mein erster Conainer"
Gut, der nächste Parameter lautet [bitmapwidth, bitmapheight]
.
Damit kann die Größe bestimmt werden, mit der alle Bitmaps im Container
dargestellt werden sollen. Wird dieser optionale Parameter nicht verwendet,
werden alle Bitmaps in ihrer jeweiligen Größe angeziegt. Wird
für beide Werte des Parameters 0 (null) angegeben, wird die Systemvorgabe
verwendet. Ich denke, daß als Einheit für diese Werte "Pixel" verwendet
wird, bin mir aber in Ermangelung klarer Angaben im Hilfematerial nicht sicher.
Kreiden Sie's mir bitte nicht an, daß ich es nicht getestet habe...;)
Um alle Bitmaps in der Größe 24 mal 24 Pixel anzuzeigen, lautet
der Wert für diesen Parameter "24,24" ...so weit ich das begriffen habe...
Dann hätten wir noch [expandbitmap, collapsebitmap]
. Mit
diesem optionalen Parameter können Sie alternative Symbole angeben, die
in den Strukturansichten OUTLINE und HIERARCHY verwendet werden, um einzelne
"Zweige" zu öffnen oder zu schließen. Sie kennen das doch wahrscheinlich
aus einem WPS-Laufwerksordner mit Strukturansicht: Durch Klicken auf ein "+"
könen Sie die Strukur eines Unterverzeichnisses einblenden. Wird dieser
Parameter nicht angegeben, werden die Vorgabewerte verwendet (eine Schaltfläche
mit einem PLUS- bzw. MINUS-Zeichen).
In DrDialog werden Bitmaps auf zwei unterschiedliche Arten angegeben - entweder
über Ihren Dateinamen, wie z.B. "c:\mybmps\bitmap01.bmp", oder durch eine
spezielle Schreibweise, um Bitmaps aus einer DLL zu laden - dazu kommen wir
in einem späteren Teil. Im Lieferumfang von DrDialog befindet sich zum
beispiel die Datei BITMAP.DLL, die eine große Zahl an Bitmaps enthält.
Um das 52. Bitmap aus dieser DLL zu verwenden, würde man anstelle eines
Dateinamens einfach "bitmap:#52" angeben.
Aber damit das hier nicht ausartet, vertrauen wir den Vorgabewerten für
die optionalen Parameter und lassen diese unter den Tisch fallen. Damit
sähe ein vollständiger Aufruf der VIEW
-Funktion wie folgt aus:
call meincontainer.view "Text", "<_Mein erster Container"
Mittlerweile wissen wir ja, daß eckige Klammern in einem Syntaxdiagramm so viel bedeuten wie "optional". Wenn Sie also beispielsweise den Titel eines Containers ändern wollen, ohne die aktuelle Darstellungsform zu ändern, lassen Sie einfach den ersten Parameter weg, aber nicht dessen Komma damit der "Parser" der Programmiersprache versteht, daß Sie einen Parameter ausgelassen haben. Das würde dann so aussehen:
call meincontainer.view , "<_Mein erster Container"
Sieht schon etwas seltsam aus, hm? Wenn Sie aber das vereinsamte Komma auch
weglassen, nimmt der Parser an, daß es sich bei"<_Mein erster Container"
um den ersten Parameter - die Darstellungsart - handelt... daher ist das Komma
ziemlich wichtig. ;)
Und da sind wir schon: Jetzt können Sie sich schon mal austoben im Ausprobieren
von Darstellungsarten und dem Format von Containertiteln. Als nächstes
schauen wir uns die Methode an, auf die schon alle sehnsüchtig warten...
...wird verwendet, um einen neuen Eintrag in einen Container aufzunehmen.
Es handelt sich wiederum um eine Funktion. Natürlich kann man sie auch
in der CALL-Schreibweise verwenden, wenn man einfach nur einen Eintrag hinzufügen
will und sich nicht für den zurückgegebenen Wert in der Funktions-Schreibweise
interessiert, aber wir sollten uns dieses mal anschauen, was da zurückgegeben
wird.
Beim Rückgabewert handelt es sich nämlich um die ID des neuen Eintrags,
die in anderen Funktionen/Methoden Verwendung findet - sogar in anderen ADD-Anweisungen:
Wenn Sie mit hierarchischen Darstellungen arbeiten, muß beispielsweise mittels
der entsprechenden ID der Eintrag angegeben werden, zu dem untergeordnete
Einträge hinzugefügt werden sollen.
Falls Sie eine "einfache" Darstellung verwenden, die nur Namen (und optional
Bitmaps) anzeigt, reicht natürlich schon
call meincontainer.add "Neuer Eintrag"
oder zusammen mit einem Bitmap namens "neu.bmp", das sich in C:\mybmps befindet...:
call meincontainer.add "Neuer Eintrag", "C:\mybmps\neu.bmp"
Um mehr Kontrolle über das Hinzufügen von Einträgen zu bekommen (z.B. um bestimmte Reihenfolgen zu erzielen), müssen wir uns die Syntax der ADD-Funktion einmal näher anschauen (das ist eine Version von mir, die sich von jener der Online-Hilfe leicht unterscheidet):
neueid = meincontainer.add(name [,bitmap] [,wo] [,referenz-id] [,info])
name |
Das Label (oder Titel, Name, Überschrift), das für den Eintrag im Container angezeigt wird. |
bitmap |
Die Bitmap, die zusammen mit dem Label des Eintrags in den Ansichten name, flowedname und bitmap angezeigt wird. |
wo |
Bestimmt die Abfolge für das Einfügen des neuen Eintrags. Dies kann einen der Werte First, Last oder Next annehmen und wird teilweise zusammen mit dem nächsten Paramter referenz-id verwendet.Wird referenz-id nicht benutzt, so fügt First den neuen Eintrag
als ersten des Containers und Last macht ihn zum letzten (dabei handelt es sich
um die Voreinstellung, wenn man wo nicht angibt).Wenn jedoch referenz-id verwendet wird, fügt Next den neuen Eintrag
nach demjenigen ein, der durch referenz-id spezifiziert wurde, während
der Gebrauch von First und Last zusammen mit referenz-id den
neuen Eintrag zum ersten oder letzten Kind des mit referenz-id angegebenen
Eintrags macht. |
referenz-id |
Verweist auf einen bereits im Container vorhandenen Eintrag. Hierbei muß es sich um die ID handeln, die dem vorhandenen Eintrag vom System zugewiesen wurde, als er dem Container hinzugefügt wurde. |
info |
Ein optionaler Parameter, der nützlich zum Abspeichern beliebiger Daten zusammen mit
dem neuen Eintrag ist. Hey! Hiermit kann man einen Schlüssel für den Datensatz in der Datenbank (oder ähnliches) für den neuen Eintag abspeichern... |
Zugegeben sind wo
und referenz-id
ziemlich "holprig" erklärt
und nicht direkt einleuchtend... lassen Sie es mich so sagen: Sofern Sie
es gerade nicht mit Eltern/Kind-Beziehungen in hierarchischen Darstellungen
zu tun haben, benötigen Sie referenz-id
eigentlich nicht. Ebensowenig benötigen Sie die first, last
oder next-Operanden des Parameters wo
.
Wenn Sie einen sortierten Container brauchen, führen Sie das Sortieren
einfach woanders durch, füllen Sie danach den Container direkt in der
gewünschten Reihenfolge und sparen Sie sich die Arbeit mit wo
und referenz-id
.
Für gewöhnlich verwende ich in solchen Fällen eine unsichtbare
Listbox, die bereits in ihrer add
-Funktion eine prima Unterstützung
für Sortierungen bietet. Dann verwende ich einfach die sortierte Liste,
um mit deren Einträgen den container zu füllen.
Wenn es allerdings darum geht, einen bestehenden und gefüllten Container
um ein weiteres einzusortierendes Element zu erweitern, ist man mit der Verwendung
der hier beschriebenen Parameter besser bedient, aber darauf kommen wir noch,
da das Hantieren mit den IDs der Container-Einträge zusätzliches
Wissen erfordert... das gleiche gilt für das Thema "z-order", das Ihnen
vielleicht bei der Lektüre des Hilfeabschnitts aufgefallen ist. Das
ist erst einmal unwichtig, lassen Sie uns fortfahren.
In der Detailansicht haben wir es mit zwei Aufgaben mehr zu tun, als nur
mit der view
und add
Funktion... wir müssen
das Spaltenlayout bestimmen und die Daten bereitstellen, die in den Tabellen-"Zellen"
(den Feldern der Spalten) erscheinen sollen. Beide Aufgaben werden mit Hilfe
der Funktion SetStem
erledigt. Das Prinzip von SetStem
besteht darin, alle benötigten Daten für die Spalten (sowohl Format
als auch Inhalt) in einer Stammvariablen zusammenzustellen, um sie dann auf
einen Schlag an den Container zu übergeben. Nur gut, daß wir schon
die Grundbegriffe der Stammvariablen kennen, hm? ;)
Wie gewöhnlich beginnen wir damit, uns die Syntax des Befehls einmal
anzuschauen, dann alles wegzuwerfen was uns im Moment nicht interessiert,
und danach den Rest genauer zu betrachten...
call meincontainer.setstem [stammname]
[, "[+/-]SELECT"] | [,"[+/-]MARK"] | [,"FORMAT" | 0 | item]
In einer besser lesbaren Form der Syntax erkennt man, daß es sich eigentlich nur um zwei Parameter handelt:
call meincontainer.setstem [stammname]
[,aktion]
[stammname]
ist der Name der Stammvariable, die die Daten für
die durchzuführende aktion
enthält. Wenn Sie keine
Stammvariable angeben, wird vom System eine Stammvariable namens "STEM" erwartet
- daher ist dieser Parameter als optional (in eckigen Klammern) dargestellt.
Für die durchzuführende aktion
stehen folgende Parameterwerte
zur Verfügung:
"[+/-]SELECT" |
Bedeutet: Entweder +SELECT oder -SELECT und wird
dazu verwendet, alle Container-Einträge an- bzw. abzuwählen, deren
IDs in den Variablen STEM.1 bis STEM.n enthalten sind.STEM.0 enthält die Anzahl der Elemente der Stammvariablen. |
"[+/-]MARK" |
Bedeutet: Entweder +MARK oder -MARK und dient
dazu, alle Containereinträge zu (de-)markieren, deren IDs in den Variablen
STEM.1 bis STEM.n enthalten sind - dabei gibt STEM.0 wieder an, aus wie vielen
Elementen die Stammvariablen besteht. |
"FORMAT" | 0 | item |
Dieser Parameter bestimmt die eigentliche
|
Wir werden uns vorerst nicht um die Themen SELECT
und MARK
kümmern, sondern uns ausschließlich dem dritten Punkt widmen.
Um also einen Container in Detailansicht zu erhalten, vergeben wir ihm mittels
der Funktion VIEW
einen Titel (oder Überschrift) und bewirken
die eigentliche Umstellung der Darstellung auf Detailansicht. Nachdem wir
das erledigt haben, sind noch drei Schritte zu erledigen, um den Container
fertigzustellen:
Und genau das ist es, wofür die dritte Form von setstem
verwendet
wird:
call setstem "formate", "FORMAT"
verwendet die Elemente einer Stammvariable namens "formate"
, um die Anzahl
und das Anzeigeformat der Spalten festzulegen.
call setstem "titel", 0
verwendet die Elemente einer Stammvariable namens "titel"
, um die Spaltenüberschriften
festzulegen.
call setstem "daten", itemid
verwendet die Elemente einer Stammvariable namens "daten"
, um die Werte in
den Spalten des Containereintrags mit der ID itemid
festzulegen.
So weit so gut. Bevor wir zu detaillierten Beispielen kommen. müssen wir uns noch kurz damit beschäftigen, wie man Spaltenformate definiert. Jedes Element der verwendeten Stammvariable legt das Format einer einzelnen Spalte fest und besteht dabei aus einem oder mehreren der folgenden Zeichen, die jeweils eine bestimmte Formatierung bewirken:
das Zeichen... | bewirkt, daß... |
---|---|
= (Gleichheitsszeichen) | ...in der Spalte Bitmaps (anstelle von Text) angezeigt
werden. Wenn dieses Zeichen in der Formatzeichenkette nicht enthalten ist, verwendet die Spalte Textausgabe. (Der Vorgabewert für das Ausgabeformat einer Spalte ist also grundsätzlich "Text" - es sei denn, dieses Zeichen wurde verwendet.) |
~ (Tilde) | ...die Spalte unsichtbar ist. Das ist sehr nützlich, um Informationen zu einem Eintrag abzulegen, die z.B. nur für programminterne Zwecke Verwendung finden. |
X | ...die Spalte schreibgeschützt ist. Standardmäßig (wenn dieses Zeichen nicht verwendet wird) kann der Spaltenwert eines Container-Eintrags vom Anwender geändert werden, indem er ihn bei gedrückter <Alt>-Taste anklickt. Das gilt jedoch nur für Spalten, deren Ausgabeformat nicht "Bitmap" ist. |
. (Punkt) | ...der Container durch eine verschiebbare, vertikale
Trennleiste in zwei Teilfenster unterteilt wird, wobei diese Spalte die letzte
(rechte) Spalte des linken Teils darstellt. Wird dieses Formatierungszeichen für keine Spalte verwendet, wird der Container ohne Trennleiste dargestellt. Existiert mehr als eine Spalte mit dieser Formatierung, wird nur der davon am meisten rechts stehenden Spalte eine Trennleiste erteilt. |
^ | ...die Spaltenwerte in ihren Zeilen nach oben ausgerichtet werden. |
V | ...die Spaltenwerte in ihren Zeilen nach unten ausgerichtet werden. |
- (minus) | ...die Spaltenwerte vertikal zentriert ausgerichtet
werden. Das ist der Standardwert; wird weder "^" noch "V" noch dieser Wert verwendet, erfolgt eine in der Zeile vertikal zentrierte Ausrichtung. |
< | ...die Spaltenwerte linksbündig ausgerichtet werden. |
> | ...die Spaltenwerte rechtsbündig ausgerichtet werden. |
| (pipe-Symbol, AltGr + "<") | ......die Spaltenwerte horizontal zentriert ausgerichtet
werden. Standardwert; wird weder "<" noch ">" noch dieser Wert verwendet, erfolgt eine horizontal zentrierte Ausrichtung. |
_ (Unterstrich) | ...die zwischen Spalte und Spaltenüberschrift
eine Linie gezogen wird. (Hinweis: Die Linie erstreckt sich dabei nicht über die gesamte Spaltenbreite sondern lediglich über die Breite der Überschrift.) |
! (Ausrufezeichen) | ...die Spalte an ihrer rechten Seite eine vertikale Trennlinie erhält. |
Um eine Spalte dazu zu bewegen, ihre Inhalte als Text und sowohl horizontal
als auch vertikal zentriert anzuzeigen, ist also keine besondere Formatierungssequenz
erforderlich, da dies alles jeweils die Vorgabewerte sind.
Um jedoch in einer Spalte linksbündige, schreibgeschützte Textausgabe
und eine vertikale Trennlinie zu erreichen, müßte die Formatierungssequenz
wie folgt aussehen:
"<X!"
...sieht auf den ersten Blick eher nach einem getunten Smiley aus, ist aber eine sehr effiziente Methode! ;)
Okay, Leute. Wo wir so weit gekommen sind... laßt uns einmal sehen, wie
also ein Container mit Detailansicht "hergestellt" wird. Ich werde alle nötigen
Schritte durchführen, um eine Ausgabe zu erreichen, die der Detailansicht-Abbildung
am Artikelanfang ähnelt. Noch ein Hinweis: Die Abschnitte, die mit einem
"/*"
beginnen und mit der umgekehrten Zeichenfolge "*/"
enden (jeweils ohne
Anführungszeichen) stellen Kommentare dar und werden vom REXX-Parser
ignoriert.
Wir gehen davon aus, daß ein Container-Steuerelement existiert, dessen
Name "cnt" lautet:
/* alle nachfolgenden Anweisungen gehören in den 'init' event handler des containers */
/*-----------------------------------------------------------------------------------*/
/* container einstellen: Detailansicht, Titel mit Trennlinie */
call cnt.view "D", "_Adressbuch"
/* Jetzt die Spalten */
/* Wir legen eine Stammvariable für die Spaltenformate an und nennen sie "formate" */
formate.0 = 5 /* fünf spalten = fünf Elemente in der Stammvariable */
formate.1 = "<X!" /* 1. spalte ist linksbündig, schreibgeschützt mit trennlinie */
formate.2 = "=!" /* 2. spalte enthält bitmaps, mit trennlinie */
formate.3 = "<." /* 3. spalte ist linksbündig mit verschiebbarer Fenstertrennleiste */
formate.4 = ">!" /* 4. spalte ist rechtsbündig mit trennleiste */
formate.5 = "|!" /* 5. spalte ist zentriert mit trennleiste */
/* der eigentliche Aufruf um die Formatierung an den container zu übergeben */
call cnt.setstem "formate", "F"
/* Spaltenüberschriften festlegen */
/* wir verwenden eine Stammvariable namens "titel" */
titel.0 = 5 /* wieder: 5 spalten = 5 elemente in der stammvar. */
titel.1 = "#"
titel.2 = "Symbol"
titel.3 = "Name"
titel.4 = "Telefonnr."
titel.5 = "email-Adresse"
/* der eigentliche Aufruf um die Überschriften an den container zu übergeben */
call cnt.setstem "titel", 0
/* jetzt legen wir die Einträge (Objekte) im Container an */
/* stellen Sie sicher, daß sich die Datei 'bitmap.dll' im selben Verzeichnis */
/* befindet wie die .RES-Datei dieses Beispiels! */
/* die "add" Funktion legt nur das Objekte mit Namen und Bitmap an. */
/* die zugehörigen spaltenwerte müssen mittels eines zusätzlichen Aufrufs */
/* von "setstem" übergeben werden, für den die ID des Eintrags benötigt wird */
/* daher verwenden wir beim Anlegen des Eintrags die "Funktionsschreibweise" */
/* und merken uns die ID des Eintrags...*/
neuereintrag = cnt.add("Peter", "bitmap:#66")
/* für die "einfachen" Darstellungstypen würde das schon reichen */
/* in der Detailansicht würde allerdings bis jetzt rein gar nichts auftauchen */
/* da wir den spalten noch keine Werte mitgeteilt haben, aber das machen wir */
/* jetzt mit der Funktion setstem und einer Stammvariable namens "daten" */
daten.0 = 5 /* fünf spalten = fünf elemente */
daten.1 = 1 /* wert für spalte 1 */
daten.2 = "bitmap:#66"
daten.3 = "Peter"
daten.4 = "555-12345"
daten.5 = "peter.mustermann@einefirma.biz"
/* jetzt der eigentliche Aufruf um die Daten an den Container zu übergeben. */
/* hier müssen wir jetzt mitteilen, für welchem Eintrag diese Spaltenwerte */
/* sein sollen... wir verwenden die ID, die uns der obige Aufruf der add-Funktion */
/* in "neuereintrag" abgelegt hat... */
call cnt.setstem "daten", neuereintrag
/* Das wär's für den ersten Eintrag... legen wir noch einen an: */
neuereintrag = cnt.add("Paul", "bitmap:#64")
daten.0 = 5
daten.1 = 2
daten.2 = "bitmap:#64"
daten.3 = "Paul"
daten.4 = "555-45678"
daten.5 = "paul.jonas@emailser.ve"
call cnt.setstem "daten", neuereintrag
/* ende des beispielcodes */
Der Grund, warum ich das alles am Stück und dafür mit so vielen Kommentaren geschrieben habe, ist, daß Sie den ganzen Abschnitt über die Zwischenablage in den code-editor von DrDialog einfügen können - und zwar in die Ereignisbehandlungsroutine "INIT" eines Containers, der "cnt" heißen muss, damit es klappt.
Als ich das erste mal mit einer Detailansicht kämpfte, zeigte mir das
Ding einfach keine Bitmaps an - egal, was ich unternahm.
Ich fand dann heraus, daß es letztlich meine Schuld war - natürlich...
denn da die zweite Spalte zur Anzeige eines Bitmaps gedacht war, hielt ich
es nicht für notwenig, im add
-Befehl auch ein Bitmap zu
übergeben, da man dieses in der Detailansicht ja ohnehin nicht sehen
würde. Das war ein Fehler. Gut, vielleicht nicht unbedingt ein Fehler,
aber diese Sparsamkeit hatte den Nebeneffekt, daß der Container scheinbar
davon ausging, es wäre kein Bitmap vorhanden. Um es zusammenzufassen:
Wenn Sie ungeachtet des Darstellungstyps irgendwas mit Bitmaps in Containern
machen wollen - geben Sie immer ein Bitmap in der add
-Anweisung
an. ;)
Hierbei handelt es sich um eine leicht verbesserte Version des obigen Beispiels.
Die komplette add/setstem
-Verarbeitung wird dabei über
eine eigene Subroutine abgewickelt, was anfänglich vielleicht nicht
gerade unbedingt zum besseren Verständnis beiträgt, die eigentliche
Lesbarkeit und Programmstruktur aber erheblich verbessert. Außerdem
demonstriert die Anwendung sehr schön, wie ein einzelner add/setstem
-Komplex
ausreicht, um alle Darstellungstypen zu gewährleisten.
Nächsten Monat schauen wir uns den "Rest" des Containerkrams an, beispielsweise wie
man Einträge löscht, selektiert oder auf bestimmte Ereignisse
reagiert.
Wenn Sie zum heutigen Thema Fragen oder Anregungen haben (oder Fehler
entdeckt haben), zögern Sie bitte nicht, mich anzumailen!
Es tut mir leid, daß ich Sie jetzt schon verlassen muß, aber ich schaffe
es sonst nicht mehr bis zum Redaktionsschluß und möchte meine Herausgeber
nicht schon wieder hängen lassen! ;)
Daten und Quellen:
|
[Artikelverzeichnis]
editor@os2voice.org
[Vorherige Seite] [Inhaltsverzeichnis] [Nächste Seite]
VOICE Homepage: http://de.os2voice.org