3. Gemeinsame Basis u_pbt_base #
← Erste Schritte · Inhalt · Designs →
Alle visuellen Komponenten erben von u_pbt_base, das den Lebenszyklus, die Eigenschaften-Engine, den Transport zur Web-Komponente und die Fehlerbehandlung bereitstellt. Die Eigenschaften selbst — einschließlich Design und Tooltips — werden von jeder Komponente veröffentlicht: die Seite der Komponente führt sie vollständig auf. Sie verwenden u_pbt_base nie direkt — Sie setzen eine konkrete Komponente ein —, aber alles Folgende steht Ihnen überall zur Verfügung.
3.1 Die Eigenschaften-Engine #
Zuweisen #
Jeder steuerbare Wert ist eine öffentliche Instanzvariable und wird direkt zugewiesen:
uo_progress.id_value = 42.5
uo_progress.is_label = "Import läuft…"
uo_progress.ib_animated = true
Das ungarische Präfix gibt den Typ an: is_ string, ib_ boolean, ii_ integer, il_ long (häufig eine RGB()-Farbe), id_ double.
Ein skalares of_set_xxx gibt es nicht: Eine Eigenschaft wird per Zuweisung gesetzt. Methoden bleiben für Hinzufügen, Entfernen und Aktionen (of_add_*, of_remove_*, of_select_*, of_reset, of_set_layout…).
Zurücklesen #
Das Lesen liefert den zuletzt gesetzten Wert (Cache auf der PowerBuilder-Seite):
if uo_progress.id_value >= 100 then …
Eine Web-Komponente lässt sich nicht synchron abfragen: Dieser Cache wird daher von den Events aufgefrischt. Immer wenn die Komponente eine Eigenschaft selbst verändert — der Benutzer folgt einem Link, zoomt mit dem Rad, klappt das Ribbon ein, tippt Text — aktualisiert das Event, das Sie benachrichtigt, die Eigenschaft gleich mit. Das Zurücklesen liefert dann den tatsächlichen Zustand, und der neue Wert steht bereits, wenn Ihr Event-Code läuft.
Dasselbe gilt für Items: Nach einem Klick des Benutzers liefert of_item(...) den Zustand, der auf dem Bildschirm steht — der ausgewählte Eintrag, der eingeklappte Abschnitt, die angehakte Schaltfläche.
Eine Eigenschaft, zu der es kein Event gibt, bleibt dagegen auf dem zuletzt von Ihnen gesetzten Wert.
Änderungen bündeln #
Eine Folge von Zuweisungen löst ebenso viele Renderings aus. of_set_redraw fasst sie zu einem einzigen zusammen:
uo_grid.of_set_redraw(/*on*/ false)
… zwanzig Zuweisungen und of_add_* …
uo_grid.of_set_redraw(/*on*/ true) // EIN einziges Neuzeichnen
Rufen Sie stets beide auf (das abschließende true ist nicht optional). Ein Lesen während des Einfrierens — of_path, eine Item-Eigenschaft — sieht, was die Folge gerade gesetzt hat: das Ausstehende wird vor der Antwort angewendet, und das Einfrieren geht danach weiter.
Über ihren Namen. Drei öffentliche Funktionen jeder visuellen Komponente ergänzen die typisierten Eigenschaften: of_set_property(name, wert) setzt jede Eigenschaft (stretch oder is_stretch, der Wert als Text: true/false, Ziffern, der RGB-Long einer Farbe; gibt 0 nach dem Anwenden zurück, -5 bei leerem Namen), of_get_property(name) liest sie live als Text, und of_component_name() gibt den Namen der eingebetteten Komponente (video, ribbon…) unabhängig von der Klasse Ihres Objekts. Das spielt eine gespeicherte Konfiguration oder ein generisches Werkzeug ab, ohne die Komponente zu kennen.
3.2 Die Items #
Eine Komponente mit Inhalt (Registerkarten, Schaltflächen, Panels, Kacheln, Abschnitte…) stellt ihre Elemente über typisierte Handles bereit, die von der Komponente selbst oder von ihrem übergeordneten Element stammen.
Hinzufügen #
Das Hinzufügen liefert das Handle des erzeugten Elements zurück:
n_pbt_tab_page lnv_page
uo_tab.of_add_page(/*key*/ "clients", /*title*/ "Kunden", /*page*/ uo_page_clients)
lnv_page = uo_tab.of_page(/*key*/ "clients")
lnv_page.is_icon = "img\clients.png"
Abrufen und ändern #
of_item(id) — oder die Factory der betreffenden Ebene — liefert das Handle eines vorhandenen Elements; seine Eigenschaften werden genau wie die einer Komponente gesetzt:
uo_toolbar.of_item(/*keys*/ "main/save").ib_enabled = false
uo_tab.of_page(/*key*/ "clients").is_title = "Kunden (128)"
Zählen, durchlaufen, prüfen #
Drei Fragen kommen immer wieder, und sie haben auf jeder Ebene dieselbe Antwort — auf der Komponente wie auf jedem Handle:
| Funktion | Antwortet |
|---|---|
of_count ( ) → long | Wie viele Elemente auf dieser Ebene gerade jetzt — was die Komponente zeigt, nicht was zuletzt gesendet wurde |
of_keys_at ( long al_index ) → string | Die Adresse des Elements an Position al_index (ab 1), oder "" jenseits beider Enden. Die vollständige Adresse, nicht der bloße Schlüssel: sie geht unverändert in jede Methode zurück, die eine erwartet — genau der Sinn, eine Ebene zu durchlaufen, die man nicht selbst gebaut hat |
of_has ( string as_key ) → boolean | Gibt es dieses Element? Ein Handle wird immer zurückgegeben, auch für einen unbekannten Schlüssel: nur so lässt sich die Frage stellen |
// Wie viele Gruppen, und wie viele Kacheln in der ersten
ll_groups = uo_tilesbox.of_count()
ls_group = uo_tilesbox.of_keys_at(/*index*/ 1)
ll_tiles = uo_tilesbox.of_count(/*keys*/ ls_group)
// Durchlaufen, was man nicht selbst gebaut hat
for li = 1 to uo_tilesbox.of_count()
ls_group = uo_tilesbox.of_keys_at(/*index*/ li)
if uo_tilesbox.of_has(/*keys*/ ls_group + "/report") then
uo_tilesbox.of_tile(/*keys*/ ls_group + "/report").ib_enabled = false
end if
next
Ein Blatt antwortet 0, "" und false: „Ich halte nichts" ist eine wahre Antwort, keine leere. Sie können also einen Baum hinabsteigen, ohne auf jeder Ebene zu prüfen.
Hierarchien: Ein Bezeichner ist nur innerhalb seines übergeordneten Elements eindeutig #
Eine mehrstufige Komponente bietet keine Abkürzung bis zum Blatt: Der vollständige Pfad ist Pflicht, wodurch garantiert ist, dass kein Bezeichner mehrdeutig ist.
// Menueband: Registerkarte > Gruppe > Steuerelement > Menueeintrag
uo_ribbon.of_item(/*keys*/ "home/clipboard/paste").ib_enabled = false
Auch die Events tragen den vollständigen Pfad:
// Event ue_clicked von uo_toolbar: (string as_keys)
choose case as_keys
case "main/save" ; of_save()
end choose
Item-Events #
Auf dem Vorfahren gibt es keine generischen Item-Events: ein Blatt-Bezeichner allein wäre mehrdeutig, sobald Items verschachtelt sind (eine Toolbar hat mehrere Leisten, eine Tilesbox mehrere Gruppen…). Jede Komponente deklariert daher ihre eigenen Item-Events mit dem vollständigen Pfad: ue_item_selected (as_section, as_key) für die Listbar, ue_tile_clicked (as_group, as_key) für die Tilesbox, ue_clicked (as_bar, as_key) für die Toolbar…
Siehe die Seite der jeweiligen Komponente: dort steht die genaue Liste.
3.3 Events, die allen Komponenten gemeinsam sind #
| Event | Ausgelöst wenn |
|---|---|
ue_ready ( ) | Die Komponente ist fertig geladen; alles zuvor Gesendete wurde nachgespielt |
ue_runtime_missing ( ) | Die WebView2-Runtime fehlt — siehe Installation |
ue_bg_color (long al_color) | Die Komponente hat ihre Design-Hintergrundfarbe berechnet; das Userobject hat diese Farbe bereits übernommen (backcolor), Sie passen bei Bedarf das Fenster an |
Befehle, die vor
ue_readygesendet werden, gehen nicht verloren: Sie werden in eine Warteschlange gestellt und der Reihe nach nachgespielt. Sie können also bereits imconstructoroder imopenalles konfigurieren.
// Event ue_bg_color: das Fenster an den Hintergrund der Komponente anpassen
parent.backcolor = al_color
3.4 Optionale Eigenschaften und Events (Opt-in) #
Manche Funktionen sind nicht standardmäßig aktiviert: Sie werden nur von den Komponenten veröffentlicht, bei denen sie sinnvoll sind, und Sie müssen sie anfordern.
Automatische Höhe — ib_auto_height #
Die Komponente misst ihre ideale Höhe und ändert die Größe des Userobjects; das Event ue_auto_height(al_height) erlaubt Ihnen, die benachbarten Steuerelemente neu zu positionieren.
uo_header.ib_auto_height = true
// Event ue_auto_height von uo_header
il_header_height = al_height
of_relayout() // positioniert den darunter liegenden Inhalt neu
Veröffentlicht von: picture und statictext.
Die Bänder veröffentlichen diese Eigenschaft nicht — ihre Höhe ist intrinsisch. ribbon und toolbar scrollen nicht vertikal: Eine fest vorgegebene Höhe kann nur Leerraum unter dem Band oder abgeschnittenen Inhalt erzeugen (eingeklapptes Menüband, auf zwei Zeilen umgebrochene Symbolleiste …). Sie passen sich deshalb immer an, ohne dass etwas zu aktivieren wäre, und lösen trotzdem ue_auto_height aus, damit Sie neu positionieren können, was darunter liegt.
Automatische Breite — ib_auto_width #
Dasselbe Prinzip für die Breite. Wird ausschließlich von listbar veröffentlicht, der einzigen Komponente, deren natürliche Breite eine Bedeutung hat.
Eine zur Symbolleiste eingeklappte listbar schrumpft von selbst und gibt die Breite beim Ausklappen wieder frei: ib_auto_width nützt Ihnen nur dann, wenn Sie auch der ausgeklappten Breite folgen wollen (die Leiste richtet sich dann nach der längsten Beschriftung).
Umgebende Maus-Events — ib_track_mouse #
Maus-Events mit hoher Frequenz werden an der Quelle abgeschnitten: Ohne Abonnement gibt die Komponente sie gar nicht aus (nichts überquert die Brücke zu PowerBuilder).
uo_button.ib_track_mouse = true // aktiviert ue_mouse_enter / ue_mouse_leave / ue_rclicked
Veröffentlicht von: button, picture, statictext.
Diskrete Events (Klick, Auswahl, Menü, Drop…) werden immer ausgegeben, ohne Abonnement.
3.5 Tastenkombinationen #
Eine Tastenkombination löst eine Komponente aus, gleichgültig wo der Fokus im Fenster liegt — der Anwender muss nicht erst zum Button zurückkehren, um ihn zu betätigen. Jede visuelle Komponente nimmt sie entgegen, ohne dass etwas aktiviert werden müsste.
uo_save.of_register_shortcut(/*chord*/ "Ctrl+S")
uo_refresh.of_register_shortcut(/*chord*/ "F5")
Eine Tastenkombination schreiben #
Die Kombination ist eine freie Zeichenkette, die von der Bibliothek normalisiert wird: Groß- und Kleinschreibung, Leerzeichen und die Reihenfolge der Modifikatoren spielen keine Rolle. "Ctrl+Shift+S", "ctrl + shift + s" und "SHIFT+CTRL+S" bezeichnen dieselbe Tastenkombination — es ist unmöglich, versehentlich zwei Varianten zu registrieren.
| Element | Zulässige Schreibweisen |
|---|---|
| Modifikatoren | Ctrl (oder Control), Alt, Shift — kombinierbar, in beliebiger Reihenfolge |
| Taste | ein Buchstabe A–Z, eine Ziffer 0–9, F1 … F24, Enter (oder Return), Escape (oder Esc), Delete (oder Del), Insert, Home, End, PageUp, PageDown; nur mit Ctrl oder Alt: + (oder Plus, Zoom Ctrl++), -, ,, ., Left Right Up Down, Space, Tab, Backspace |
Diese Liste ist abschließend: Eine nicht aufgeführte Taste (Tab, Leertaste, eine Taste des Ziffernblocks, ein Satzzeichen) löst keine Tastenkombination aus.
Eine einzelne Taste ist eine gültige Kombination ("F5"). Eine leere Zeichenkette entfernt die Tastenkombination von der Komponente.
"Enter"und"Escape"lassen sich allein nicht als Tastenkombination registrieren: Diese beiden Tasten bleiben dem Standard-Button und dem Abbrechen-Button vorbehalten (ib_default/ib_canceldes button). In Verbindung mit einem Modifikator werden sie wieder zu gewöhnlichen Kombinationen ("Ctrl+Enter").
Wer bei einem Konflikt gewinnt #
Zwei Komponenten dürfen dieselbe Kombination anfordern — das kommt häufig vor, wenn ein Fenster mehrere Bereiche beherbergt, die jeweils ihr eigenes „Speichern“ haben. Die Entscheidung fällt in dieser Reihenfolge:
- die dem Tastaturfokus nächste Komponente gewinnt: die, die den Fokus selbst hat, sonst die im selben MDI-Blatt, im selben Bereich, auf derselben Registerkartenseite wie das fokussierte Steuerelement — die Tastenkombination eines aktiven Bereichs wird niemals von einem Nachbarn verdeckt;
- bei Gleichstand zwischen zwei Komponenten (keine ist näher als die andere) erhält sie keine: die Taste bleibt beim Fenster;
- zwischen zwei Kürzeln derselben Komponente gewinnt das zuerst registrierte;
- die Alt+Buchstaben der Beschriftungen (Mnemoniks einer Schaltfläche) werden erst nach allen ausdrücklichen Kürzeln befragt.
Dieselbe Regel gilt für Enter und Esc: Bei zwei MDI-Blättern mit je einer Standardschaltfläche geht Enter an die des Blatts, in dem getippt wird. Enter bleibt dagegen bei einem fokussierten PB-CommandButton, und Enter oder Esc bei einer geöffneten DropDownListBox.
Es treten nur die sichtbaren und aktiven Komponenten des Fensters im Vordergrund an. Wird eine Kombination auf einer Komponente erneut registriert, die bereits eine hatte, so ersetzt sie diese, ohne deren Rang zu ändern: Ein Fenster neu zu konfigurieren mischt die Prioritäten nicht neu.
Tastenkombinationen für Items #
Die Überladung mit zwei Argumenten bindet die Kombination an ein Item der Komponente statt an die gesamte Komponente — das zweite Argument ist der Bezeichner des Items; bei einer Komponente mit mehreren Ebenen seine Adresse (eine toolbar nimmt "main/save", oder "main/export/pdf" für einen Menüeintrag):
uo_bar.of_register_shortcut(/*chord*/ "Ctrl+N", /*keys*/ "new")
uo_bar.of_register_shortcut(/*chord*/ "Ctrl+P", /*keys*/ "print")
Tastenkombinationen entfernen #
uo_bar.of_clear_shortcuts() // Komponente UND Items
of_reset() und das Zerstören der Komponente rufen of_clear_shortcuts() für Sie auf: Eine verschwundene Komponente behält niemals eine Kombination reserviert.
Die Alt-Taste #
Alt allein wird nicht abgefangen: Sie gibt den Fokus an das Menüband, das daraufhin seine Keytips einblendet (siehe ribbon). Die Bibliothek fängt die nachfolgenden Tastenanschläge also nicht ab — das Menüband liest sie, so als hätte der Anwender es angeklickt. Esc oder ein zweites Alt geben den Fokus an das verlassene Steuerelement zurück. Deklariert kein Menüband des Fensters einen Keytip, behält Alt sein gewohntes Windows-Verhalten.
| Element | Wirkung |
|---|---|
of_register_shortcut (string as_chord) | Deklariert eine Tastenkombination für die Komponente; eine leere Zeichenkette entfernt sie |
of_register_shortcut (string as_chord, string as_key) | Deklariert eine Tastenkombination für ein Item, bezeichnet durch seinen Bezeichner |
of_clear_shortcuts ( ) | Entfernt alle Tastenkombinationen der Komponente, einschließlich der Items |
3.6 Eine Komponente zurücksetzen: of_reset() #
of_reset() versetzt die Komponente in ihren Neuzustand zurück, so als wäre sie gerade geladen worden:
- der Inhalt wird geleert (Items, Seiten, Panels…);
- jede Eigenschaft kehrt auf ihren Standard zurück (Formatierung, Farben, Modus, Beschriftungen);
- die auf der Instanz gesetzten Style-Overrides und Tooltips werden aufgehoben;
- der Eigenschaften-Cache auf der PowerBuilder-Seite wird geleert (das Zurücklesen beginnt wieder bei den Standardwerten);
- auch der native Zustand wird zurückgesetzt (Kontextmenü, Anzeigemodus…).
uo_grid.of_reset() // mit einem leeren Raster neu beginnen
// ... und dann neu aufbauen
⚠️ Wenn Sie eine Instanz für etwas anderes wiederverwenden, ohne
of_reset()aufzurufen, bleibt der vorherige Zustand erhalten (eine Farbe, ein Modus, eine automatische Höhe). Das ist die häufigste Ursache für einen unerklärlichen „Anzeigerest“.
3.7 Diagnose #
| Element | Wirkung |
|---|---|
of_is_created ( ) → boolean | Die native Komponente existiert (Runtime vorhanden, Host gültig) |
of_is_ready ( ) → boolean | Der Web-Inhalt ist geladen (ue_ready bereits ausgelöst) |
of_get_last_error ( ) → string | Letzte ausführliche Fehlermeldung der DLL, nach einem Rückgabewert < 0 |
Rückgabecodes der of_*-Methoden:
| Rückgabe | Bedeutung |
|---|---|
≥ 0 | OK (angewendet oder in die Warteschlange gestellt) |
-2 | Komponente nicht erstellt (Runtime fehlt, Host ungültig) |
-4 | Vorgang fehlgeschlagen (Bildschirmfoto, Schreiben einer Datei…) |
-5 | Ungültiges Argument (leerer Bezeichner, Wert außerhalb des Bereichs) |
-6 | WebView2-Runtime zu alt für die angeforderte Funktion (Drucken) |
3.8 Die Darstellung als Bild exportieren #
Jede Komponente kann sich als Bild exportieren, genau so, wie sie angezeigt wird:
uo_tiles.of_save_as_png(/*path*/ "C:\temp\accueil.png")
uo_tiles.of_save_as_jpg(/*path*/ "C:\temp\accueil.jpg")
Zum Drucken statt zum Exportieren siehe Drucken.
Praktisch für einen Bericht, einen E-Mail-Anhang oder einen Störungsnachweis. Die Komponente muss erstellt und ihr Inhalt geladen sein.
3.9 Lebenszyklus #
- Erstellung: Die WebView wird bereits bei der Konstruktion des Userobjects erzeugt — unerlässlich für das Hosting (Registerkarten, andockbare Panels): Eine WebView, die nach dem Umhängen ihres HWND erzeugt wird, wird nicht angezeigt.
- Warteschlange: Ihre Befehle werden in eine Warteschlange gestellt, solange
ue_readynicht ausgelöst wurde. - Bereit:
ue_ready; die Warteschlange wird der Reihe nach nachgespielt. - Größenänderung: automatisch, die Komponente folgt der Größe des Userobjects.
- Zerstörung: beim Schließen des Fensters; die WebView wird freigegeben, kein verwaister Prozess bleibt zurück.
Rufen Sie PBT_Warmup() einmal beim Start der Anwendung auf, damit dieser Zyklus unbemerkt bleibt (Installation).
3.10 Best Practices #
- Setzen Sie das Standarddesign und die Sprache im Anwendungsobjekt, bevor das erste Fenster geöffnet wird: Die Komponenten zeigen dann kein Aufblitzen des Stils.
- Klammern Sie jeden umfangreichen Aufbau mit
of_set_redraw(false)/of_set_redraw(true)ein. - Rufen Sie
of_reset()auf, bevor Sie eine Instanz für einen anderen Inhalt wiederverwenden. - Blockieren Sie den UI-Thread nicht durch eine lange PowerScript-Schleife zwischen Erstellung und Anzeige: Die Initialisierung der WebView benötigt die Nachrichtenschleife (siehe FAQ).