# 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(false)
    … zwanzig Zuweisungen und of_add_* …
    uo_grid.of_set_redraw(true)     // EIN einziges Neuzeichnen

Rufen Sie stets beide auf (das abschließende `true` ist nicht optional).

## 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("clients", "Kunden", uo_page_clients)
    lnv_page = uo_tab.of_item("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_bar("main").of_item("save").ib_enabled = false
    uo_tab.of_item("clients").is_title = "Kunden (128)"

### 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_tab("home").of_group("clipboard").of_item("paste").ib_enabled = false

Auch die Events tragen den vollständigen Pfad:

    // Event ue_clicked von uo_toolbar: (string as_bar, string as_id)
    choose case as_bar + "/" + as_id
        case "main/save" ; of_enregistrer()
    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_id)` für die Listbar, `ue_tile_clicked (as_group, as_id)` für die Tilesbox, `ue_clicked (as_bar, as_id)` 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_ready` gesendet werden, gehen nicht verloren: Sie werden in eine Warteschlange gestellt und der Reihe nach nachgespielt. Sie können also bereits im `constructor` oder im `open` alles 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_entete.ib_auto_height = true

    // Event ue_auto_height von uo_entete
    il_hauteur_entete = 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_bouton.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_enregistrer.of_register_shortcut("Ctrl+S")
    uo_actualiser.of_register_shortcut("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`

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_cancel` des 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:

1. die Komponente mit dem Tastaturfokus setzt sich gegen alle anderen durch: Die Tastenkombination eines aktiven Bereichs wird niemals von einem Nachbarn verdeckt;
2. andernfalls gewinnt die zuerst registrierte.

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:

    uo_barre.of_register_shortcut(/*Kombination*/ "Ctrl+N", /*Item*/ "nouveau")
    uo_barre.of_register_shortcut(/*Kombination*/ "Ctrl+P", /*Item*/ "imprimer")

### Tastenkombinationen entfernen #

    uo_barre.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_pivot.of_save_as_png("C:\temp\tableau.png")
    uo_pivot.of_save_as_jpg("C:\temp\tableau.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 #

1. 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.
2. Warteschlange: Ihre Befehle werden in eine Warteschlange gestellt, solange `ue_ready` nicht ausgelöst wurde.
3. Bereit: `ue_ready`; die Warteschlange wird der Reihe nach nachgespielt.
4. Größenänderung: automatisch, die Komponente folgt der Größe des Userobjects.
5. 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).

← Erste Schritte · Inhalt · Designs →