Vorwort
Dieses Dokument beschreibt die im Package com.cisag.pgm.gui enthaltenen Klassen. Es ist eine Ergänzung zur API-Dokumentation (JavaDoc).
Das Dokument ist ein Referenzhandbuch, welches einen schnellen Überblick bietet und spezifische Informationen über die enthaltenen Klassen in dem Package com.cisag.pgm.gui liefert. Um grundlegende Kenntnisse zu vermitteln, enthält es ein Einführungs-Kapitel über wichtige Komponenten und Mechanismen der GUI-Programmierung. Den Abschluss bildet ein Kapitel mit verschiedenen Programmbeispielen.
Zielgruppe
Dieses Dokument richtet sich an Software-Entwickler, die mithilfe der Klassen aus dem Package com.cisag.pgm.gui grafische Benutzerschnittstellen (GUIs) für Semiramis-Anwendungen entwickeln wollen.
Es wird davon ausgegangen, dass der Leser mit der Programmiersprache Java und der Entwicklungsumgebung von Semiramis vertraut ist. Eine Einführung in die Entwicklung von Anwendungen für Semiramis ist in der Dokumentation „Programmierhandbuch“ zu finden.
Weiterhin werden Grundkenntnisse in der Entwicklung von grafischen Oberflächen für moderne Softwaresysteme vorausgesetzt.
Einführung
Für die Gestaltung von grafischen Oberflächen stellt Semiramis ein Framework von Java-Klassen zur Verfügung, welches in vielen Bereichen mit dem AWT bzw. Swing Konzept von Java vergleichbar ist. Programmierkenntnisse und -techniken aus diesen Bereichen können meist direkt angewandt und umgesetzt werden. Das Java API (Application Programming Interface) verbirgt dabei die Client/Server-Umgebung genauso wie die Tatsache, dass es sich bei Clients z. B. um einen Browser handelt, der nur HTML und JavaScript versteht.
Das Semiramis UI-Framework besteht aus zwei Java Packages:
- cisag.pgm.gui und
- cisag.pgm.dialog.
Das Package com.cisag.pgm.gui stellt das eigentliche API bereit und das Package com.cisag.pgm.dialog die Implementierung dazu.
Mit com.cisag.pgm.gui werden die GUIs für Anwendungen „gebaut“ und com.cisag.pgm.dialog wird in der Regel dazu verwendet, das Framework um weitere Komponenten zu erweitern. Im Fokus des Handbuchs steht jedoch die Anwendungsentwicklung, daher wird auf das zweite Package im Folgenden nur sehr eingeschränkt eingegangen.
Das Grundprinzip ist analog zu AWT: Ein GUI setzt sich aus verschiedenen Komponenten (VisualElement) zusammen. Typische Komponenten sind
- Textfelder,
- Label und
- Buttons.
Eine spezielle Art von Komponente stellen die Container (VisualElementContainer) dar, sie dienen dazu andere Komponenten zu gruppieren, zu positionieren und gemeinsam zu steuern. Dazu werden die zusammengehörigen Komponenten dem entsprechenden Container hinzugefügt (add).
Da Container selbst auch Komponenten sind, können auch sie wiederum einem (übergeordneten) Container hinzugefügt werden. Ein GUI stellt eine Baumstruktur bestehend aus Containern und anderen Komponenten dar, wobei der oberste Container jeweils ein Fenster (Window) oder eine davon abgeleitete Klasse ist.
Ein zweites Grundprinzip (ebenfalls analog zu AWT) stellen die Layout-Manager dar. Statt den Komponenten feste Größen und Koordinaten zu zuordnen, erfolgt die Berechnung von Größe und Position dynamisch, d. h. die Werte werden für den jeweiligen Inhalt, die verwendete Schriftgröße, die Fenstergröße und die Bildschirmauflösung automatisch berechnet und bei Veränderungen angepasst.
Jedem Container ist ein Layout-Manager zugeordnet und der übernimmt die Aufgabe der Platzierung. Ein Layout-Manager implementiert sozusagen die Strategie nach der die Komponenten eines Containers platziert werden und regelt auch wie der Platz, der dem Container zur Verfügung steht, genutzt wird.
Anders als die universelleren APIs (AWT, Swing, com.cisag.pgm.dialog) bietet das Package com.cisag.pgm.gui auch spezielle Unterstützung für das Programmieren von Standardanwendungen in Semiramis. Hierzu zählen die Verwendung von Metadaten (Repository), die Implementierung von Style-Guides sowie die Vereinfachung von Standardaufgaben.
2.1 Merkmale und Konzepte
2.1.1 Elemente und Container-Hierarchie
Ein kleines Beispiel mit häufig benutzten Elementen soll verdeutlichen, wie aus einzelnen Elementen eine (Container-) Hierarchie aufgebaut wird.
- Beispiel Dialog
In diesem Beispiel werden vier Standardelemente aus com.cisag.pgm.gui verwendet:
- Dialog (cisag.pgm.gui.Dialog),
- View (cisag.pgm.gui.View),
- Button (cisag.pgm.gui.Button)und
- Label (cisag.pgm.gui.Label).
Dialog ist ein „top-level“-Container:
Es wird der Platz für alle enthaltenen Elemente zur Verfügung gestelt. Weitere häufig benutzte „top-level“-Container sind z. B. Anwendungsfenster sowie Popup-Windows.
View ist ein „Zwischen“- oder „Hilfs“-Container:
Es wird damit die Positionierung des Buttons und des Label realisiert. Andere „Zwischen-“ oder „Hilfs“-Container, wie z. B. ScrollPane oder TabbedPane bieten darüber hinaus auch zusätzliche (z. B. interaktive) Eigenschaften.
Button und Label gehören zu den „atomaren Elementen“, d. h. sie dienen nicht dazu andere Elemente aufzunehmen, sondern stellen in der Regel Informationen dar. Häufig dienen sie dazu, die Eingaben von Benutzer entgegen zunehmen, wie z. B. bei Textfeldern, ComboBoxen und Tabellen.
Das nachfolgende Diagramm zeigt die Container-Hierarchie des Beispiels. Es enthält jeden erzeugten oder benutzen Container zusammen mit den enthaltenen Elementen. Es ist zu beachten, dass jedes Window (auch jeder Dialog) eine eigene, unabhängige Container-Hierarchie bildet.
- Container-Hierarchie
In dem Diagramm ist zu sehen, dass schon ein einfaches Beispiel eine Container-Hierarchie mit mehreren Ebenen besitzt. Die Wurzel einer Container-Hierarchie ist immer ein „top-level“-Container.
Jeder „top-level“-Container enthält (manchmal auch nur indirekt) einen „Zwischen“- bzw. „Hilfs“-Container, der als „content-pane“ bezeichnet wird. Es ist in den meisten Programmen unwichtig, ob und welche Container zwischen dem „top-level“-Container und seinem „content-pane“ tatsächlich liegen. Im Allgemeinen enthält das „content-pane“ (direkt oder indirekt) alle sichtbaren Elemente eines Fensters. Eine Ausnahme stellt das Anwendungsfenster dar, es enthält noch weitere Bereiche (CoolBar, StatusBar, Navigator, …).
Um einem Container eine Komponente hinzuzufügen, ist eine der verschiedenen „add“-Methoden zu benutzen. Die „add“-Methode benötigt mindestens ein Argument (das Element, welches hinzugefügt werden soll). Manchmal ist ein weiteres Argumente notwendig, um nähere Informationen für das Layout zu spezifizieren. Im nachfolgenden Code-Beispiel wird dies z. B. dazu genutzt, um den View in der Mitte des Containers (content-pane) zu platzieren.
Mit folgendem Code lässt sich der oben abgebildete Dialog erzeugen:
dialog = new Dialog(…);
button = new Button(…);
label = new Label(…);
view = new View(…);
view.add(button);
view.add(label);
dialog.getContentPane().add(view, …);
2.1.2 Layout-Management
Das Berechnen der Größe und Position von Elementen innerhalb eines Containers wird als Layout-Management bezeichnet. Jeder Container besitzt einen Layout-Manager. Das ist ein Objekt, welches die Layout-Berechnungen für die, in dem Container liegenden Elemente durchführt.
Die Elemente liefern dem Layout-Manager Informationen bezüglich der von ihnen benötigten Größe oder Ausrichtung. Auf Basis der Layout-Strategie entscheidet der Layout-Manager, ob diese Informationen berücksichtigt werden.
Die beiden Beispiele mit jeweils fünf Buttons zeigen, wie sich das GUI von Programmen in Abhängigkeit von den verwendeten Layout-Managern ändert:
- BorderLayout
- BoxLayout
Das UI-Framework stellt fünf Layout-Manager bereit (siehe 3.9 Layout-Manager):
- BorderLayout,
- BoxLayout,
- FlowLayout,
- StandardLayout und
- XYLayout.
Die ersten drei Layouts verhalten sich analog zu den gleichnamigen Layout-Managern in AWT bzw. Swing.
Das StandardLayout ist der häufigst benutzte Layout-Manager in Semiramis und bietet spezielle Unterstützung für mehrspaltige Layouts mit Feldern sowie Container-übergreifende Fluchtlinien (Guides).
Das XYLayout erlaubt die „pixel-genaue“ Platzierung von Elementen, sollte allerdings nur verwendet werden, wenn Elemente „layouted“ werden sollen, die nicht abhängig von Schriften (-größen) und/oder Bildschirmauflösungen sind.
Immer wenn mit „add“ ein Element in einen Container eingefügt wird, muss der Layout-Manager berücksichtigt werden, der bei dem Container gesetzt ist. Die Layout-Manager (BorderLayout, StandardLayout, XYLayout) benötigten zusätzliche Informationen (auch als „Contraints“ bezeichnet) für die Positionierung in dem Container. Diese Informationen sind als zusätzlicher Parameter bei der „add“-Methode zu übergeben. Die anderen Layout-Manager (BoxLayout, FlowLayout) platzieren die Elemente einfach in der Reihenfolge, wie sie mit „add“ hinzugefügt wurden.
2.1.2.1 Layout-Manager setzen
Anders als in AWT bzw. Swing muss bei den Containern des UI-Frameworks immer ein Layout-Manager gesetzt sein. Die absolute Positionierung setLayout(null) wird nicht unterstützt. Mit dem XYLayout kann es umgangen werden, aber davon ist generell abzuraten, da sich solche Layouts bei der Änderung der Fenstergröße nicht anpassen und zudem abhängig von bestimmten Bildschirmauflösungen und/oder Schriftarten sind.
Das Setzen eines Layout-Managers sollte immer unmittelbar bei der Erzeugung des Containers erfolgen, insbesondere bevor dem Container Elemente hinzugefügt werden. Ein Layout-Manager kann auch nachträglich geändert werden, allerdings sollten dann vorher alle Elemente entfernt und anschließend wieder hinzugefügt werden.
Das folgende Code-Fragment zeigt, wie man bei einem Container ein BorderLayout setzen kann:
View view = new View();
view.setLayout(new BorderLayout());
2.1.2.2 Hinweise für den Layout-Manager
Die Elemente können dem Layout-Manager Hinweise bzw. Wünsche für ihre Größe oder Ausrichtung liefern. Hierzu besitzen alle VisualElements die Eigenschaften
- preferredWidth, preferredHeight,
- minimumWidth, minimumHeight,
- maximumWidth, maximumHeight,
- alignmentX und alignmentY.
Die unterschiedlichen Layout-Manager berücksichtigen diese Eigenschaften je nach ihrer Strategie vollständig, teilweise oder überhaupt nicht.
Die Standardwerte für die genannten Eigenschaften berechtet jedes Element selbstständig anhand seines Typs und/oder Inhalts. Der Programmier hat die Möglichkeit, jede der genannten Eigenschaften mit eigenen Werten zu übersteuern z. B. mit setMaximumWidth(). Ein Überschreiben der Getter ist allerdings nicht möglich, bzw. führt nicht zum erwünschten Ergebnis.
2.1.2.3 Abstände
Es gibt verschiedene Möglichkeiten, um Elemente nicht dicht an dicht, sondern mit Abständen zu einander zu platzieren:
- Einige Layout-Manager unterstützen das direkt, z. B. durch entsprechende Eigenschaften (z. B. hgap, vgap)
- Einfügen von „unsichtbaren“ Elementen. Insbesondere für die Verwendung mit dem BoxLayout stehen hierfür die Elemente Glue und Strut zur Verfügung.
- Verwenden von „Margins“. Hiermit kann für ein Element oder einen Container festgelegt werden, wie groß der Abstand zwischen seinem Rand und seinem Inhalt sein soll.
- Verwenden von EmptyBorder. Der Randabstand ergibt sich hierbei aus der Stärke eines „unsichtbaren“ Rahmens. Vom Ergebnis ist das gleich zur Verwendung von Margins, wegen der Klarheit sollten allerdings die Margins verwendet werden.
2.1.3 VisualElement
Die Klasse com.cisag.pgm.gui.VisualElement ist die Basisklasse für alle visuellen Komponenten aus dem GUI-Framework. Die Klasse definiert die grundlegenden Eigenschaften von allen Elementen – ähnlich wie es
- awt.Component für AWT und
- swing.JComponent für Swing
tun.
Zu diesen grundlegenden Eigenschaften zählen z. B.:
- Identifizierung
- Darstellungseigenschaften
- Zustände (enabled, editable, visible)
- Unterstützung (Hinweise) für Layout-Manager
- Unterstützung für Tastatursteuerung
- Unterstützung für Metadaten
- Ereignisbehandlung
- ToolTips, Direkthilfe und Kontextmenüs
2.1.3.1 Identifizierung
Jedem VisualElement soll bei der Instanziierung eine GUID zugeordnet werden. Mithilfe dieser GUID kann das betreffende Element jederzeit identifiziert werden. Dies ist notwendig, wenn später für ein Element spezielle Berechtigungen festgelegt werden sollen. Wichtig ist, dass einerseits den zu unterscheidenden VisualElements tatsächlich unterschiedliche GUIDs zugeordnet werden und anderseits für die zu identifizierenden Elemente die GUID immer konstant ist. Wenn eine Anwendung ein Feld „Artikelnummer“ instanziiert, dann wird diesem Feld eine GUID zugeordnet. Jede Instanz dieser Anwendung hat nun eine eigene Instanz von „Artikelnummer“, aber diese Instanzen haben die gleiche GUID. Wenn in derselben Anwendung ein zweites Feld, z. B. „Preis“, instanziiert wird, dann muss es eine andere GUID haben als das Feld für die „Artikelnummer“. Verwendet schließlich eine andere Anwendung ebenfalls ein Feld „Artikelnummer“, so ist i.d.R. davon auszugehen, dass es sich um ein anderes Feld handelt und deshalb auch die GUIDs unterschiedlich sein müssen.
Die Spezifikation der GUID ist nur im Konstruktor der jeweiligen Elemente, d. h. den abgeleiteten Klassen von VisualElement, möglich und kann nachträglich nicht geändert werden. In der aktuellen Version von Semiramis unterstützen einige Subklassen (z. B. Button) die Angabe von GUIDs noch nicht.
Um die Vergabe von GUIDs zu vereinfachen, unterstützt die Entwicklungsumgebung den Wert com.cisag.pgm. datatype.Guid.AUTOGUID. Diese Konstante kann an Stellen verwendet werden, an denen (einmalig) eine GUID zu vergeben ist. Beim „Einchecken“ wird dann automatisch eine neue GUID berechnet und der Text com.cisag.pgm.datatype.Guid.AUTOGUID damit ersetzt.
TextField field =
new TextField(Guid.AUTOGUID, …);
- B. wird ersetzt durch:
TextField field = new TextField(
“01E09C573EC51E10AC1C8464641F0000”, …);
2.1.3.2 Metadaten
Bei der Entwicklung von Anwendungen für Semiramis spielen Metadaten eine wichtige Rolle. Das hat auch starken Einfluss auf die Art und Weise wie das GUI einer Semiramis-Anwendung zu programmieren ist. Die Metadaten (z. B. für Attribute bzw. logische Datentypen) werden im Repository gepflegt, deshalb gilt es Redundanzen im GUI-Code zu vermeiden. Ansonsten würde es bei Änderungen immer zu mehrfacher Pflege führen, was den Sinn der Metadaten teilweise aufheben würde.
Das UI-Framework unterstützt die Erzeugung von GUI-Elementen anhand von Metadaten. Um z. B. ein Textfeld so zu konfigurieren, dass es ein bestimmtes Attribut von einem Business-Objekt darstellt, reicht es aus, im Konstruktor den (voll qualifizierten) Namen des Attributes anzugeben. Damit bekommt das Textfeld automatisch das richtige Label, einen Tool-Tip, eine Direkthilfe, ggf. auch eine Suche und/oder ein Kontextmenü.
Eine ähnliche Unterstützung gibt es auch für Buttons und andere Elemente. Nur werden bei Buttons oder Menüeinträgen nicht die Metadaten eines Attributs, sondern die einer „Action“ referenziert. Um die Lokalisierung der Texte braucht sich der Programmierer dabei nicht zu kümmern, diese Aufgabe wird von dem Framework automatisch durchgeführt.
Im Allgemeinen ist bei Instanziierung von GUI Elementen im Konstruktor ein „Pfad“ zu übergeben. Dieser Pfad spezifiziert ein „Entwicklungsobjekt“ aus dem Repository, dessen Daten für die Initialisierung des GUI-Elements verwendet werden sollen. Je nach GUI-Element kommen dafür unterschiedliche Entwicklungsobjekte in Frage. Hier eine (unvollständige) Übersicht:
- Fields:
Attribut, logischer Datentyp, DataDescription
- Actions, Buttons:
Action
- Icon:
Icon
- Label, GroupBox, …:
StringTable
In Verbindung mit Containern ist es auch möglich mit „relativen“ Pfaden zu arbeiten, d. h. für den Container wird eine Art „Basispfad“ spezifiziert und bei den darin liegenden Feldern nur noch der Rest des Pfades. Im Dokument „Programmierhandbuch“ wird genauer auf logische Datentypen eingegangen und im Dokument „Data-Description“ ist beschrieben, welche UI-Eigenschaften im Repository definiert werden können.
Das UI-Framework nutzt für die Repräsentation von Metadaten die Klassen:
- cisag.pgm.dialog.model.DataDescription
- cisag.pgm.dialog.model.ActionDescription
Die entsprechenden Instanzen werden anhand des Pfades automatisch erstellt. Es besteht auch die Möglichkeit eigene Instanzen von DataDescription zu erstellen und damit Felder zu initialisieren.
2.1.4 Ereignisbehandlung (Actions)
Die Ereignisbehandlung in com.cisag.pgm.gui weicht etwas von dem Modell in AWT/Swing ab:
- Statt einer Vielzahl von unterschiedlichen Event-Typen und Event-Listenern gibt es nur die Klassen cisag.pgm.gui.Action als Event und com.cisag.pgm.gui.ActionListener als Event-Listener.
- Ähnlich wie bei swing.Action besitzen Instanzen der Klasse com.cisag.pgm.gui.Action einen Zustand (z. B. enabled) und Informationen für die Visualisierung (Text, Icons, ToolTips). Anders als bei javax.swing.Action ist diese Funktionalität nicht dem Listener, sondern dem Event selbst zugeordnet.
- Alle Actions besitzen eine Identifikation („id“) vom Typ „int“, das erleichtert die zentrale Ereignisbehandlung mithilfe von „switch“-Anweisungen.
- Einige Elemente (z. B. Buttons) können ausschließlich über Actions erzeugt werden.
2.1.5 Look & Feel
Ähnlich wie Swing unterstützt Semiramis ein austauschbares „Look & Feel“, d. h. das Aussehen und das Verhalten der Oberfläche (GUI) lassen sich in gewissen Grenzen verändern bzw. nach eigenen Bedürfnissen (Geschmack) anpassen. Die aktuelle Version von Semiramis erlaubt dem Benutzer z. B. zwischen verschiedenen „Themen“ (Themes) zu wählen, wobei sich die aktuellen Themen nur in der Farbzusammenstellung unterscheiden. Weiterhin kann der Anwender Einfluss auf die Schriftgröße (bzw. Auflösung) nehmen.
Ungeachtet der Möglichkeit, dass ein Anwender die Oberfläche nach seinen Wünschen anpassen kann, sollten alle Anwendungen in Semiramis möglichst ein einheitliches Aussehen und Verhalten haben. Aus diesem Grund wurden entsprechende Style-Guides für Semiramis-Anwendungen definiert.
Das UI-Framework in Semiramis unterstützt diese Anforderungen indem die Look & Feel relevanten Eigenschaften der Elemente, soweit wie möglich, automatisch (ggf. dynamisch) ermittelt bzw. vorbelegt werden.
Das explizite Setzen oder Ändern von diesen Eigenschaften (z. B. Farbe, Abstände, Rahmen, Schriftart, etc.) ist im Allgemeinen nicht notwendig und möglicherweise sogar schädlich, wenn es den Style-Guides oder den Einstellungen durch den Anwender widerspricht.
Für die Fälle, die in dem UI-Framework bzw. den Style-Guides nicht explizit berücksichtigt sind, kann der Programmierer über die Klasse com.cisag.pgm.gui.UIManager indirekt, d. h. über symbolische Namen auf einen Pool von Standardfarben, -abständen und -schriftarten zugreifen. Auf diese Weise bleibt gewährleistet, dass Anpassungen durch den Benutzer weiterhin berücksichtigt werden.
2.1.6 Tastatursteuerung
Um die Benutzer von Semiramis bei der Bedienung optimal zu unterstützen, sollen möglichst alle Funktionen auch „mouse-less“ mittels Tastatursteuerung angeboten werden. Gerade bei sich wiederholenden Tätigkeiten (z. B. Datenerfassung) fördert eine durchgängige Tastatursteuerung das effiziente Arbeiten. In dem Semiramis-Styleguide sind weitere Hinweise zu diesem Thema zu finden.
Das UI-Framework unterstützt die Tastatursteuerung auf verschiedenen Ebenen:
- Fokussteuerung,
- Access Keys und
2.1.6.1 Fokussteuerung
Der Tastaturfokus bestimmt das aktive UI-Element, auf das sich die nächste Tastatureingabe des Benutzers auswirkt. Neben dem Anklicken mit der Maus kann der Benutzer das fokussierte Element auch mit der Tastatur wechseln. Die Fokussteuerung ist damit eine alternative Navigationsmöglichkeit.
Das UI-Framework verwendet einen „Fokus-Manager“, der den Fokuswechsel per Tastatur steuert bzw. kontrolliert. Seine Basisfunktionalität besteht darin, die Navigation zwischen den Oberflächenelementen mithilfe der Tastatur zu ermöglichen. Dazu verwendet der Fokus-Manager, wie für diesen Zweck üblich, die „TAB“-Taste:
- Mit „TAB“ kommt man von einem Element zum „nächsten“ Element.
- Und mit „SHIFT+TAB“ kommt man zum „vorherigen“ Element.
Die (TAB-)Reihenfolge der Elemente wird dabei normalerweise vom Fokus-Manager automatisch bestimmt. Der Fokus-Manager leitet die Reihenfolge im Wesentlichen aus der Platzierung der Elemente auf dem Bildschirm ab. Wobei er zeilenweise von links nach rechts und von oben nach unten vorgeht. Unsichtbare, nicht editierbare oder „disabled“ Elemente werden automatisch ausgenommen (übersprungen). Als Programmierer hat man aber die Möglichkeit dieses Verhalten zu beeinflussen:
- Mit setFocusTraversable(boolean b) kann für ein Element festgelegt werden, ob es überhaupt mit „TAB“ bzw. „SHIFT+TAB“ erreichbar sein soll. Bei false wird das betreffende Element bei „TAB“ übersprungen, kann i.d.R. noch mit der Maus „angeklickt“ werden.
Der Fokus-Manager berücksichtigt auch die Elementeigenschaft focusCycleRoot. Bei Elementen mit Subelementen (Container, Tabellen) bewirkt diese Eigenschaft, dass der Tastaturfokus das betreffende Element nicht verlässt, sondern zwischen dessen Subelementen „kreist“. Ein Verlassen ist dann nur mit „CTRL+G“ bzw. „CTRL+SHIFT+G“ möglich. In der aktuellen Version von Semiramis lässt sich diese Eigenschaft mit isFocusCycleRoot() zwar abfragen, aber nicht ändern.
2.1.6.2 Access Keys (Mnemonics)
Für alle Elemente mit Beschriftung (Felder, Buttons, Menüeinträge) kann ein „Access Key“ (Mnemonic) definiert werden. Dabei handelt es sich um eine alphanumerische Taste, die, wenn zusammen mit der „ALT“-Taste verwendet, zum jeweiligen Oberflächenelement navigiert und dieses aktiviert bzw. den Eingabefokus setzt. Die Definition eines Access Keys erfolgt durch das Einfügen von „&“ (Ampersand) vor dem gewünschten Zeichen, innerhalb des Beschriftungstextes. Um dem Benutzer zu signalisieren, ob und mit welchem Access Key das Element erreichbar bzw. aktivierbar ist, wird in dem Beschriftungstextes das entsprechende Zeichen durch eine Unterstreichung hervorgehoben (Ein Beispiel ist in Abbildung 3-5 zu sehen).
Üblicherweise werden die Beschriftungen von Elementen im Repository (DataDescription bzw. Action) definiert. Bei dem zugehörigen Entwicklungsobjekt ist das „&“ einfach in dem Feld für „Label“ einzutragen.
2.1.6.3 Shortcuts (Accelerators)
Shortcuts (Accelerators) sind Tasten oder Tastenkombinationen, die Benutzer verwenden können, um auf häufig benutzte Funktionen schnell zugreifen zu können. „Strg“+Taste und die Funktionstasten (F1 bis F12) sind i.d.R. am besten als Shortcuts geeignet. Hinweise für die richtige Verwendung von Shortcuts sind in dem Dokument „Semiramis-Styleguide“ zu finden. Eine Übersicht über die bereits verwendeten/belegten Shortcuts ist in dem Dokument „Bedienungsleitfaden“ zu finden.
Die Definition (Registrierung) von Shortcuts erfolgt mithilfe der Methoden registerKeyStroke(Action action) bzw. registerKeyStroke(String keyStroke, Action action) an der Klasse com.cisag.pgm.gui.VisualElement.
Bei der Methode registerKeyStroke(Action action)wird die Tastenkombination (KeyStroke) aus den Metadaten der Action (Repository) ermittelt. Und bei der Methode registerKeyStroke(String keyStroke, Action action)direkt durch den Programmierer angegeben wird. Für die Spezifizikation von keyStroke ist bei „druckbaren“ Zeichen (ASCII 33 bis 127) direkt das entsprechende Zeichen, bei nicht druckbaren Zeichen bzw. Funktionstasten die englische Bezeichnung (in Großbuchstaben) zu verwenden. Beispiele hierfür sind:
- „a“,
- „ENTER“,
- „SPACE“ oder
- „F2“.
Wenn statt einer einzelnen Taste eine Tastenkombination definiert werden soll, dann lässt sich das z. B. durch
- „CTRL+F2“,
- „ALT+SHIFT+F10“ oder
- „ALT+SHIFT+a“
ausdrücken.
Das Element bei dem ein Shortcut mit registerKeyStroke registriert wird, stellt zugleich den Kontext dar, in dem dieser Shortcut Wirkung zeigt. Der Tastaturfokus muss sich in diesem Element oder (bei Containern) in einem Subelement befinden, damit die registrierte Action beim Eingeben des Shortcuts funktioniert. Es ist durchaus möglich zwei unterschiedliche Actions auf denselben Shortcut zu legen, wenn die Registrierung bei unterschiedlichen Elementen erfolgt. Ob und welche Action funktioniert hängt dann davon ab, ob und welches Element den Tastaturfokus hatte.
Das manuelle Registrieren von ShortCuts ist nur selten notwendig, da die meisten in Frage kommenden Funktionen (Actions) automatisch registriert werden. Das gilt z. B. für alle Actions, die als Button oder SmartButton in der „MainCoolBar“ eingefügt werden. In diesen Fällen übernimmt der „MainCoolBar“ die Registrierung des im Repository hinterlegten Shortcuts. Ähnlich verhalten sich auch die Symbolleisten von Listen und Tabellen.
2.1.7 Meldungen
Für Semiramis Anwendungen gilt, dass Dialoge (insbesondere modale) nur dann zu benutzen sind, wenn es wirklich darauf ankommt, dass der Benutzer „genau jetzt“ eine Entscheidung treffen muss. Beispielsweise, wenn er ein neues Objekt laden will, aber die geänderten Daten des vorherigen Objektes noch nicht gespeichert hat.
Grundsätzlich gilt, dass der Benutzer selbst entscheiden soll, in welcher Reihenfolge er seine Eingaben macht. Das gilt auch, wenn das System festellen sollte, dass einige der Eingaben nicht korrekt/gültig sind. Der Benutzer soll keinesfalls in dem fehlerhaften Feld gefangen bleiben, bis er einen „richtigen“ Wert eingegeben hat. Auch soll er nicht durch „aufdringliche“ Dialoge in der flüssigen Erfassung behindert werden (wichtige Hinweise zu diesem Thema sind auch in dem Dokument Semiramis-Styleguide zu finden).
In Semiramis werden Meldungen in einer speziellen Meldungsseite (im Navigationsbereich) und/oder in der Statuszeile angezeigt. Bei feldbezogenen Meldungen wird zusätzlich auch das entsprechende Feld markiert („rote Ecke“).
Bei den Meldungen (mit Feldbezug) wird zwischen zwei Arten unterschieden:
- Meldungen, die durch (generische) Prüfungen vom System erzeugt werden und
- Meldungen, die der Anwendungsprogrammierer in den von ihm programmierten Prüfungen selbst erzeugt.
Das System führt seine Prüfungen anhand der Metadaten durch. Wobei es sich überwiegend um Typ-, Syntax- bzw. Längenprüfungen handelt. Wenn z. B. per DataDescription festgelegt ist, dass maximal 10 Zeichen zulässig sind, dann würde das betreffende Feld automatisch mit einer „roten Ecke“ markiert, wenn der Feldinhalt dieses Limit überschreiten sollte. Diese Art von Prüfungen findet in der Regel bereits beim Verlassen des Feldes statt, spätestens beim nächsten Round-Trip (Kommunikation mit dem Server).
Anwendungsspezifische Meldungen basieren auf entsprechenden Prüfungen in der Anwendungslogik bzw. speziellen „Prüfklassen“ (Validations). Im Unterschied zu den generischen Prüfungen durch das System, finden diese Prüfungen nur bei bestimmten Benutzeraktionen statt, z. B. „Speichern“ oder „Prüfen“. Ein weiterer, wichtiger Unterschied liegt darin, dass diese Prüfungen bzw. deren Meldungen explizit den entsprechenden Feldern zugeordnet werden müssen, da die Prüfklassen prinzipiell UI-unabhängig sein müssen. Das Dokument „Programmierhandbuch enthält im Kapitel „Meldungen“ eine detaillierte Beschreibung über das Registrieren und Senden von Meldungen mit Codebeispielen.
2.2 Dialoganwendungen
In Semiramis gibt es verschiedene Arten von Anwendungen. Die Anwendungen mit grafischer Benutzerschnittstelle (GUI) werden auch als Dialoganwendungen bezeichnet. Ein anderer Anwendungstyp sind beispielsweise die Hintergrundanwendungen (Batch-Anwendungen), die kein GUI besitzen.
In Semiramis wird das Hauptanwendungsfenster vom System bereitgestellt. Das System bzw. die Rahmenanwendung (JobControl) ist auch für die anwendungsüber-greifende Navigation zuständig. Damit die verschiedenen Dialoganwendungen nebeneinander in Semiramis funktionieren, müssen sie bestimmte Voraussetzungen erfüllen bzw. Standards einhalten. Für Dialoganwendungen in Semiramis gilt daher die Vorgabe, dass sie von der abstrakten Basis-Klasse com.cisag.pgm.base.CisUiApplication abgeleitet werden müssen. Diese Elternklasse befreit den Anwendungsentwickler einerseits von Standardaufgaben und stellt anderseits auch den Zugriff auf die vorgegebenen Bereiche im Hauptanwendungsfenster (Navigator/Locator, CoolBar, IdentArea und WorkArea) bereit.
Eine Dialoganwendung hat normalerweise mindestens die folgenden drei Methoden zu implementieren:
- Die Methode init() wird vom System zur Initialisierung der Anwendung aufgerufen.
- Die Methode performAction() dient der Behandlung auftretender Ereignisse.
- Mittels der run()-Methode können der Anwendung auch Startparameter übergeben werden.
Die Initialisierung des GUI erfolgt üblicherweise in der init()-Methode, d. h. in dieser Methode werden die notwendigen Elemente erzeugt und die Container-Hierarchie aufgebaut. Die Elternklasse CisUiApplication stellt über die Methoden getIdentPane(), getWorkPane() und getMainCoolBar() die Container bereit, in die die eigenen Elemente eingefügt werden können.
2.3 Spezielle Elemente
Das Package com.cisag.pgm.gui beinhaltet eine Reihe von speziellen Elementen, die dem Anwendungsprogrammierer helfen sollen, Standardaufgaben leichter und Styleguide konform umzusetzen. Hierzu zählen z. B. Listen, Tabellen und EntityFields.
2.3.1 Listen und Tabellen
In Semiramis wird zwischen Listen und Tabellen unterschieden. Tabellen werden i.d.R. benutzt, wenn sich aus Spalten und Zeilen eine eindeutige „Gitterstruktur“ ergibt. Listen lassen sich hingegen auch verwenden, wenn jede Zeile aus anderen Daten besteht bzw. wenn Daten über mehrere Zeilen darzustellen sind.
Sowohl Listen als auch Tabellen berücksichtigen, dass in einer ERP-Anwendung nicht alle Daten gleichzeitig angezeigt werden können. Mithilfe eines sog. Seitenmodells (PageModel) kann der Benutzer durch die Daten „blättern“ und der Anwendungsprogrammierer die anzuzeigenden Daten nachladen.
Das Datenmodell von Listen und Tabellen basiert auf einer Liste von Datensätzen. Ein solcher Datensatz wird jeweils durch eine Instanz der Klasse com.cisag.pgm.base.obj.CisListPartMutable repräsentiert. Dieses Objekt dient sowohl als generischer „Datencontainer“, d. h. es kann eine Referenz auf ein beliebiges (Business) Objekt speichern, als auch der Speicherung von zusätzlichen Informationen (Status) über den jeweiligen Datensatz. Beispiele für solche Informationen sind Selektion, Lösch- oder Änderungsmarkierungen sowie Hinweise für die Visualisierung.
Während das Datenmodell immer alle geladenen[1] Datensätze beinhaltet, ist die Anzeige auf dem Bildschirm auf eine „Seite“ beschränkt. Die Größe einer Seite, also die Anzahl von Datensätzen pro Seite, kann durch den Programmierer festgelegt werden und sollte immer so gewählt werden, dass die Liste/Tabelle vollständig gefüllt ist und keine Scrollbalken notwendig sind[2].
Die Visualisierung der zu einer Seite gehörenden Datensätze, erfolgt bei beiden Elementen zeilenweise. Die eigentliche Visualisierung bzw. das Mapping der Daten (Attribute) auf UI-Elemente wird an externe Objekte delegiert. Bei der Liste sind es die ListViews (siehe 3.6.2.3 ListView) und bei der Tabelle der TableDataManager (siehe 3.6.3.1 TableDataManager). Diese Objekte sind auch für das (ebenfalls zeilenweise) „Rückwärts-Mapping“, also die Übernahme der Benutzereingaben aus den UI-Elementen in die Datensätze verantwortlich.
Im Dokument „Programmierhandbuch“ ist die Funktionsweise und Verwendung von Listen bzw. Tabellen ausführlich beschrieben (Kapitel „Arbeiten mit Listen“ bzw. „Arbeiten mit Tabellen“), entsprechende Beispielanwendungen werden mit den Klassen com.cisag.app.edu.ui.BookListInquiry bzw. com.cisag.app.edu.ui.BookTableMaintenance bereitgestellt.
Das Dokument Semiramis Styleguide macht für bestimmte Anwendungsfälle (Suchen, nur Anzeige, mit/ohne Eingabemöglichkeit, …) entsprechende Vorgaben, die bei Listen bzw. Tabellen zu beachten sind.
2.3.2 EntityFields
EntityFields sind Spezialfelder für die Identifikation von Business Entities. Sie erlauben neben der Eingabe der eindeutigen Identifikation für ein Business Entity (Schlüsselattribute) auch die Anzeige einer Beschreibung zu dem Entity. Darüberhinaus stellen EntityFields eine Reihe weiterer Funktionen für das angezeigte Entity bereit:
- Wertehilfe
- Links auf zugordnete Anwendungen
- Kontextmenü
- Unterstützung für „Drag & Drop“
Das Package com.cisag.pgm.gui stellt diese Funktionalität in Form von abstrakten Basisklassen zur Verfügung: com.cisag.pgm.gui.EntityField und com.cisag.pgm.gui.SimpleEntityField. Für jedes Entity ist eine eigene, angepasste Implementierung bereitzustellen, wobei je nach Komplexität des Entities entweder com.cisag.pgm.gui.EntityField (siehe 3.5.4 EntityField) oder com.cisag.pgm.gui.SimpleEntityField als Elternklasse zu verwenden ist. Weitere Informationen sind in dem Dokument „Programmierhandbuch“ (Kapitel „Entity-Felder“) zu finden.
2.4 Erweitern und Anpassen von GUI Elementen
Grundsätzlich ist es möglich, die Klassen in com.cisag.pgm.gui durch Vererbung zu erweitern. Wenn möglich, ist die existierenden Klassen über die vorhandenen Methoden zu konfigurieren.
Beim Überschreiben von Methoden ist zu beachten:
- Die ursprüngliche Implementierung muss mittels „super“ aufgerufen werden.
- Das Überschreiben von manchen „Gettern“ (z. B. getPreferredSize()) funktioniert nur bedingt, da die Implementierung (cisag.pgm.dialog.*) nicht über diese Methoden zugreift.
2.4.1 Verbindung zu com.cisag.pgm.dialog
Praktisch alle abgeleiteten Klassen von com.cisag.pgm.gui.VisualElement delegieren die eigentliche Implementierung an eine entsprechende Klasse aus dem Package com.cisag.pgm.dialog. Intern wird zugehörige Instanz meist durch eine (protected) Variable mit dem Namen „x“ repräsentiert.
Zu beachten ist, dass die Initialisierung der Variablen „x“ nicht bei allen Elementen bereits im Konstruktor erfolgt, sondern teilweise erst mit dem Hinzufügen („add“) zu einem Container. Zu dieser Art von Elementen gehören beispielsweise alle von com.cisag.pgm.gui.Field abgeleiteten Klassen. Dieses Verhalten ergibt sich aus der Tatsache, dass die für die Erzeugung der „x“-Elemente notwendigen Metadaten erst beschafft werden können, wenn der Pfad zu den Metadaten vollständig bekannt ist. Bei relativen Pfaden ist eine vollständige Berechnung des Pfades aber erst über den Container möglich. Wenn eine Methode an einem Element aufgerufen wird, bevor das „x“-Element initialisert wurde, kann es daher zu einer java.lang.NullPointerException kommen.
Von „außen“ kann über die Hilfsklasse com.cisag.pgm.gui.WebInterface auf das „x“-Element zugegriffen werden. Grundsätzlich ist von der Verwendung dieser Möglichkeit jedoch dringend abzuraten, da diese Eigenschaft nicht zu der „öffentlichen“ Schnittstelle der Klassen in com.cisag.pgm.gui zählt und sich jederzeit ändern oder auch ganz entfallen kann. Wenn überhaupt, so sollte die Verwendung von com.cisag.pgm.gui.WebInterface das letzte Mittel sein. Sie wird hier nur erwähnt, weil sie vielleicht noch in einigen (alten) Anwendungen verwendet wird.
[1]Ob die Datensätze komplett oder nur bei Bedarf z. B. seitenweise (nach)geladen werden, kann durch den Programmierer festgelegt werden.
[2]Listen und Tabellen unterstützen auch die automatische Anpassung der Zeilenanzahl an die Bildschirmgröße (siehe 3.6.1.3 Seitenmodell).