feat: content import system - Markdown parser, importer, 7 decks + 3 tutorials + 5 cheatsheets
Dieser Commit ist enthalten in:
Ursprung
1c59b667f2
Commit
6a504254b0
17 geänderte Dateien mit 2658 neuen und 0 gelöschten Zeilen
289
edu/content/tutorials/01-omnis-kickstart.md
Normale Datei
289
edu/content/tutorials/01-omnis-kickstart.md
Normale Datei
|
|
@ -0,0 +1,289 @@
|
|||
# Tutorial 1: Omnis Kickstart - Von Null zum ersten Verstaendnis
|
||||
|
||||
**Dauer**: ca. 30 Minuten
|
||||
**Voraussetzung**: Grundkenntnisse in irgendeiner Programmiersprache (JS, Python, VB, C#, Java...)
|
||||
**Ziel**: Du verstehst die Grundstruktur von Omnis und kannst einfachen Code lesen
|
||||
|
||||
---
|
||||
|
||||
## 1. Was ist Omnis Studio?
|
||||
|
||||
Stell dir vor: Visual Studio + Access + Node.js in einem Tool. Das ist Omnis.
|
||||
|
||||
| Was du kennst | Omnis-Aequivalent |
|
||||
|---------------|-------------------|
|
||||
| VS Code / IDE | Omnis Studio IDE (integriert) |
|
||||
| .sln / Projekt | Library (.lbs Datei) |
|
||||
| Klassen / Dateien | Classes (in der Library) |
|
||||
| Funktionen | Methods |
|
||||
| npm packages | Externe Libraries |
|
||||
| HTML/React | Windows (Desktop) oder Remote Forms (Web) |
|
||||
| SQL Workbench | Eingebauter SQL Browser |
|
||||
|
||||
**Der groesste Unterschied:** Omnis ist eine **Kommando-basierte Sprache**, keine Expression-basierte. Jede Zeile beginnt mit einem Befehl.
|
||||
|
||||
---
|
||||
|
||||
## 2. Die Sprache: Befehle statt Ausdruecke
|
||||
|
||||
### In JavaScript:
|
||||
```javascript
|
||||
let name = "Harry"; // Deklaration + Zuweisung
|
||||
let total = price * quantity; // Ausdruck
|
||||
console.log(name); // Funktionsaufruf
|
||||
```
|
||||
|
||||
### In Omnis:
|
||||
```omnis
|
||||
Calculate lName as "Harry" ;; Zuweisung mit dem Befehl "Calculate"
|
||||
Calculate lTotal as lPrice * lQty ;; Berechnung mit "Calculate"
|
||||
OK message {[lName]} ;; Nachricht anzeigen (wie alert/console.log)
|
||||
```
|
||||
|
||||
**Merke:**
|
||||
- `Calculate` = "Berechne und weise zu"
|
||||
- Variablen haben Praefix: `l` = lokal, `i` = Instanz, `t` = Task, `p` = Parameter
|
||||
- `;;` oder `#` fuer Kommentare
|
||||
- `{[variable]}` fuer String-Interpolation in Texten
|
||||
|
||||
---
|
||||
|
||||
## 3. Variablen: Das Praefix-System
|
||||
|
||||
In Omnis hat JEDE Variable einen Praefix, der ihren **Scope** anzeigt:
|
||||
|
||||
| Praefix | Scope | Wie in JS/Python |
|
||||
|---------|-------|-----------------|
|
||||
| `l` | Lokal (nur diese Methode) | `let x` / lokale Variable |
|
||||
| `i` | Instanz (dieses Objekt) | `this.x` / `self.x` |
|
||||
| `t` | Task (globale Session) | Globale Variable |
|
||||
| `p` | Parameter (Eingabe) | Funktionsparameter |
|
||||
| `c` | Klassen-Variable (statisch) | `static` / Klassenvariable |
|
||||
|
||||
```omnis
|
||||
# Beispiel: Methode mit Parameter und lokaler Variable
|
||||
Calculate lFullName as con(pFirstName, " ", pLastName)
|
||||
Calculate iCustomerName as lFullName ;; In Instanz-Var speichern
|
||||
```
|
||||
|
||||
**Uebung 1:** Was ist der Scope dieser Variablen?
|
||||
- `lCounter` -> ???
|
||||
- `iOrderList` -> ???
|
||||
- `tCurrentUser` -> ???
|
||||
- `pAmount` -> ???
|
||||
|
||||
<details>
|
||||
<summary>Loesung</summary>
|
||||
|
||||
- `lCounter` -> Lokal (nur in der aktuellen Methode)
|
||||
- `iOrderList` -> Instanz (gehoert zum Objekt)
|
||||
- `tCurrentUser` -> Task (global in der Session)
|
||||
- `pAmount` -> Parameter (wurde der Methode uebergeben)
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 4. Datentypen
|
||||
|
||||
| Omnis Typ | Konstante | Wie in JS |
|
||||
|-----------|-----------|-----------|
|
||||
| Character (String) | kCharacter | string |
|
||||
| Integer | kInteger | number (int) |
|
||||
| Number (Dezimal) | kNumber | number (float) |
|
||||
| Boolean | kBoolean | boolean |
|
||||
| Date | kDate | Date |
|
||||
| List | kList | Array of Objects |
|
||||
| Row | kRow | Object / Record |
|
||||
| Object Reference | kObjectref | Referenz auf ein Objekt |
|
||||
|
||||
```omnis
|
||||
# Variablen werden im IDE deklariert (nicht im Code!)
|
||||
# Im IDE: Variable "lName" anlegen, Typ = Character
|
||||
# Dann im Code:
|
||||
Calculate lName as "Harry"
|
||||
Calculate lAge as 42
|
||||
Calculate lActive as kTrue
|
||||
```
|
||||
|
||||
**Wichtig:** Variablen werden im Omnis IDE deklariert (wie in VB mit Dim), NICHT im Code!
|
||||
|
||||
---
|
||||
|
||||
## 5. Kontrollstrukturen
|
||||
|
||||
### If / Else
|
||||
```omnis
|
||||
;; JavaScript: if (age >= 18) { ... } else { ... }
|
||||
;; Omnis:
|
||||
If lAge >= 18
|
||||
OK message {Erwachsen}
|
||||
Else
|
||||
OK message {Minderjaehrig}
|
||||
End If
|
||||
```
|
||||
|
||||
### For-Schleife
|
||||
```omnis
|
||||
;; JavaScript: for (let i = 1; i <= 10; i++) { ... }
|
||||
;; Omnis:
|
||||
For lCounter from 1 to 10 step 1
|
||||
Calculate lSum as lSum + lCounter
|
||||
End For
|
||||
```
|
||||
|
||||
### While-Schleife
|
||||
```omnis
|
||||
;; JavaScript: while (condition) { ... }
|
||||
;; Omnis:
|
||||
While lCounter < 100
|
||||
Calculate lCounter as lCounter + 1
|
||||
End While
|
||||
```
|
||||
|
||||
### Switch/Case
|
||||
```omnis
|
||||
;; JavaScript: switch(status) { case 'A': ... }
|
||||
;; Omnis:
|
||||
Switch lStatus
|
||||
Case 'A'
|
||||
# Aktiv
|
||||
Case 'I'
|
||||
# Inaktiv
|
||||
Default
|
||||
# Unbekannt
|
||||
End Switch
|
||||
```
|
||||
|
||||
**Uebung 2:** Uebersetze diesen JavaScript-Code in Omnis:
|
||||
```javascript
|
||||
let sum = 0;
|
||||
for (let i = 1; i <= 100; i++) {
|
||||
if (i % 2 === 0) {
|
||||
sum += i;
|
||||
}
|
||||
}
|
||||
console.log("Summe: " + sum);
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Loesung</summary>
|
||||
|
||||
```omnis
|
||||
Calculate lSum as 0
|
||||
For lI from 1 to 100 step 1
|
||||
If mod(lI, 2) = 0
|
||||
Calculate lSum as lSum + lI
|
||||
End If
|
||||
End For
|
||||
OK message {Summe: [lSum]}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 6. Methoden (Funktionen)
|
||||
|
||||
In Omnis sind Methoden immer Teil einer Klasse (wie in Java/C#, nie wie freie Funktionen in JS/Python).
|
||||
|
||||
### Methode aufrufen:
|
||||
```omnis
|
||||
;; JavaScript: let result = myObject.calculate(10, 20);
|
||||
;; Omnis:
|
||||
Do iMyObject.$calculate(10, 20) Returns lResult
|
||||
```
|
||||
|
||||
### Das `Do` Kommando:
|
||||
- `Do` = "Fuehre aus" (wie ein Funktionsaufruf)
|
||||
- `$methodname` = Dollar-Praefix fuer Methoden
|
||||
- `Returns lVar` = Fange den Rueckgabewert auf
|
||||
|
||||
### Methode definieren (im IDE):
|
||||
Methoden werden im Omnis IDE erstellt, nicht im Code deklariert.
|
||||
```omnis
|
||||
# Methode "$calculate" mit Parametern pA, pB:
|
||||
Calculate lResult as pA + pB
|
||||
Quit method lResult ;; = return lResult
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Die $-Notation: Das Herz von Omnis
|
||||
|
||||
Das ist DER groesste Unterschied zu allen anderen Sprachen. In Omnis adressierst du ALLES ueber Dollar-Pfade:
|
||||
|
||||
```omnis
|
||||
;; Objekt-Property lesen:
|
||||
Calculate lVal as $cinst.$objs.myField.$contents
|
||||
|
||||
;; Aufgeschluesselt:
|
||||
;; $cinst = aktuelle Instanz (= this)
|
||||
;; .$objs = alle UI-Objekte darauf
|
||||
;; .myField = das Feld namens "myField"
|
||||
;; .$contents = dessen Inhalt
|
||||
```
|
||||
|
||||
### Die wichtigsten $-Pfade:
|
||||
|
||||
| Pfad | Bedeutung | JS-Aequivalent |
|
||||
|------|-----------|----------------|
|
||||
| `$cinst` | Aktuelle Instanz | `this` |
|
||||
| `$cclass` | Aktuelle Klasse | `this.constructor` |
|
||||
| `$clib` | Aktuelle Library | - |
|
||||
| `$ctask` | Aktueller Task | - |
|
||||
| `$objs` | UI-Objekte auf einem Window | `document.querySelectorAll` |
|
||||
| `$contents` | Wert/Inhalt | `.value` / `.textContent` |
|
||||
| `$visible` | Sichtbarkeit | `.style.display` |
|
||||
| `$enabled` | Aktiv/Inaktiv | `.disabled` |
|
||||
|
||||
### Property SETZEN (nicht = sondern $assign!):
|
||||
```omnis
|
||||
;; JavaScript: element.style.display = 'none';
|
||||
;; Omnis:
|
||||
Do $cinst.$objs.myField.$visible.$assign(kFalse)
|
||||
|
||||
;; JavaScript: element.value = "Hello";
|
||||
;; Omnis:
|
||||
Do $cinst.$objs.myField.$contents.$assign("Hello")
|
||||
```
|
||||
|
||||
**Das ist anfangs verwirrend, wird aber schnell natuerlich!**
|
||||
|
||||
---
|
||||
|
||||
## 8. Zusammenfassung: Die 7 goldenen Regeln
|
||||
|
||||
1. **Jede Zeile ist ein Befehl**: `Calculate`, `Do`, `If`, `For`, etc.
|
||||
2. **Variablen haben Scope-Praefixe**: `l`okal, `i`nstanz, `t`ask, `p`arameter
|
||||
3. **$ ist ueberall**: Methoden, Properties, Pfade - alles mit Dollar
|
||||
4. **$assign() zum Setzen**: Nie `=` fuer Notation-Properties
|
||||
5. **Variablen werden im IDE deklariert**: Nicht im Code
|
||||
6. **`Do` fuer Aufrufe, `Calculate` fuer Berechnungen**: Die zwei Hauptbefehle
|
||||
7. **`Returns` / `#F` fuer Ergebnisse**: Rueckgabewerte und der globale Flag
|
||||
|
||||
---
|
||||
|
||||
## Mini-Quiz: Teste dich selbst!
|
||||
|
||||
**Frage 1:** Was macht dieser Code?
|
||||
```omnis
|
||||
Calculate lList as $cinst.$objs.orderGrid.$dataname
|
||||
```
|
||||
|
||||
**Frage 2:** Wie liest man die `$visible`-Property eines Feldes namens "btnSave"?
|
||||
|
||||
**Frage 3:** Was ist der Unterschied zwischen `Calculate` und `Do`?
|
||||
|
||||
<details>
|
||||
<summary>Antworten</summary>
|
||||
|
||||
1. Holt den Datennamen (= gebundene Variable) des UI-Elements "orderGrid" auf der aktuellen Instanz.
|
||||
2. `Calculate lVisible as $cinst.$objs.btnSave.$visible`
|
||||
3. `Calculate` = Berechnung/Zuweisung, `Do` = Methodenaufruf/Befehlsausfuehrung
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
**Naechstes Tutorial:** [02 - Notation Mastery](02-notation-mastery.md) - Tiefer eintauchen in das $-Notationssystem
|
||||
457
edu/content/tutorials/08-window-hierarchy-deepdive.md
Normale Datei
457
edu/content/tutorials/08-window-hierarchy-deepdive.md
Normale Datei
|
|
@ -0,0 +1,457 @@
|
|||
# Deep-Dive: Fensterprogrammierung in Solution2
|
||||
|
||||
## Warum dieses Tutorial?
|
||||
|
||||
Die Fensterprogrammierung in Solution2 ist einer der **komplexesten Bereiche** des Systems. Das liegt am **rekursiven SubWindow-Muster**: Fenster enthalten SubWindows, die wiederum SubWindows enthalten koennen - und alle muessen koordiniert kommunizieren.
|
||||
|
||||
Dieses Tutorial erklaert die Architektur von Grund auf, mit echtem Code aus dem Solution2-Repository.
|
||||
|
||||
---
|
||||
|
||||
## 1. Die 3-Stufen-Klassenhierarchie
|
||||
|
||||
Solution2 hat **drei zentrale Fenster-Superklassen**, alle in der `solution2`-Library unter `SuperClasses`:
|
||||
|
||||
```
|
||||
wBaseWindow <-- Wurzel (keine Superklasse)
|
||||
|
|
||||
+-- wMainWindow <-- Hauptfenster (Auftraege, Teile, Rechnungen...)
|
||||
|
|
||||
+-- wSubWindow <-- Eingebettete Panels (Tab-Inhalte, Detail-Ansichten)
|
||||
```
|
||||
|
||||
### Visuelle Darstellung der Architektur
|
||||
|
||||
```
|
||||
+============================================================+
|
||||
| wMainWindow (z.B. Sales/wOrders) |
|
||||
| extends wBaseWindow |
|
||||
| |
|
||||
| Instanzvariablen: |
|
||||
| ioCode -> oOrders (Business-Logik) |
|
||||
| iiRef2SubWindow -> Referenz auf aktives SubWindow |
|
||||
| ibControl2SubWindow -> true/false (Wer hat Kontrolle?) |
|
||||
| ilList, irRow -> Haupt-Datenliste/Zeile |
|
||||
| inEDMode -> Aktueller Bearbeitungsmodus |
|
||||
| |
|
||||
| +------------------------------------------------------+ |
|
||||
| | TabPane "TP" | |
|
||||
| | | |
|
||||
| | +-----------------------------------------------+ | |
|
||||
| | | wSubWindow (z.B. Sales/wOrder) | | |
|
||||
| | | extends wBaseWindow | | |
|
||||
| | | | | |
|
||||
| | | Instanzvariablen: | | |
|
||||
| | | iiRef2Parent -> zurueck zum MainWindow | | |
|
||||
| | | iiRef2SubWindow -> eigenes SubWindow | | |
|
||||
| | | ibControl2SubWindow -> true/false | | |
|
||||
| | | ioCode -> eigene Business-Logik | | |
|
||||
| | | icFormWindow -> Name fuer Rechtepruefung | | |
|
||||
| | | | | |
|
||||
| | | +------------------------------------------+ | | |
|
||||
| | | | wSubWindow (Ebene 3, z.B. wOrderItems) | | | |
|
||||
| | | | extends wBaseWindow | | | |
|
||||
| | | | | | | |
|
||||
| | | | iiRef2Parent -> zurueck zu wOrder | | | |
|
||||
| | | | ioCode -> oOrderItems | | | |
|
||||
| | | +------------------------------------------+ | | |
|
||||
| | +-----------------------------------------------+ | |
|
||||
| +------------------------------------------------------+ |
|
||||
+============================================================+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. wBaseWindow - Die Wurzel
|
||||
|
||||
`wBaseWindow` ist die Wurzel-Superklasse ALLER Solution2-Fenster. Sie definiert die **gemeinsame Infrastruktur**:
|
||||
|
||||
### Wichtige Instanzvariablen (geerbt von ALLEN Fenstern)
|
||||
|
||||
| Variable | Typ | Bedeutung |
|
||||
|----------|-----|-----------|
|
||||
| `ioCode` | Object-Ref | Business-Logik-Objekt (z.B. `oOrders`) |
|
||||
| `iiRef2SubWindow` | Item-Ref | Referenz auf das aktive SubWindow |
|
||||
| `ibControl2SubWindow` | Boolean | `kTrue` = SubWindow hat Kontrolle |
|
||||
| `ilList` | List | Haupt-Datenliste des Fensters |
|
||||
| `irRow` | Row | Aktuelle Daten-Zeile |
|
||||
| `irRowSearch` | Row | Such-Kriterien |
|
||||
| `inEDMode` | Integer | Aktueller Modus (Anzeige/Edit/Insert/...) |
|
||||
| `icCurrTabPane` | Char | Name des aktuell aktiven Tab-Reiters |
|
||||
|
||||
### Wichtige Methoden auf wBaseWindow
|
||||
|
||||
| Methode | Zweck |
|
||||
|---------|-------|
|
||||
| `$EnableTabPane(pEDMode)` | Sperrt/Entsperrt Tab-Reiter basierend auf Modus |
|
||||
| `$setRowName(pName)` | Setzt den Namen der Row-Variable |
|
||||
| `$setListName(pName)` | Setzt den Namen der List-Variable |
|
||||
| `$translateObjects($cinst)` | Uebersetzt UI-Beschriftungen |
|
||||
|
||||
### $EnableTabPane - Code
|
||||
|
||||
```omnis
|
||||
## wBaseWindow.$EnableTabPane:
|
||||
# Freigabe und Sperren von TabPane in Abhaengigkeit von angewaehltem Bearbeitungsmodus
|
||||
Do $cwind.$objs.TP.$enableTabPanes(pEDMode)
|
||||
```
|
||||
|
||||
> **Merke**: Das TabPane-Objekt heisst immer `TP` - das ist Konvention in Solution2!
|
||||
|
||||
---
|
||||
|
||||
## 3. wMainWindow - Das Hauptfenster
|
||||
|
||||
`wMainWindow` erbt von `wBaseWindow` und ist die Superklasse fuer alle **eigenstaendigen Dateneingabe-Fenster** (Auftraege, Teile, Rechnungen, etc.).
|
||||
|
||||
### Zusaetzliche Instanzvariablen
|
||||
|
||||
| Variable | Typ | Bedeutung |
|
||||
|----------|-----|-----------|
|
||||
| `irRowCount` | Row | Zaehler fuer Datensaetze |
|
||||
| `irRowMax` | Row | Maximum-Zaehler |
|
||||
| `irStatusBarPane` | Row | Statusleisten-Daten |
|
||||
|
||||
### Das Command-Routing-Pattern
|
||||
|
||||
**DAS ist das zentrale Muster!** Jeder Befehl (Edit, Save, Cancel, Delete, Print...) wird nach dem gleichen Schema delegiert:
|
||||
|
||||
```omnis
|
||||
## wMainWindow.$cCancel:
|
||||
Do $cinst.$closeRelatedWindow() ## 1. Zugehoerige Fenster schliessen
|
||||
If $cinst.ibControl2SubWindow ## 2. Wer hat die Kontrolle?
|
||||
Do iiRef2SubWindow.$cCancel() ## -> SubWindow!
|
||||
Else
|
||||
Do $cinst.ioCode.$cCancel() ## -> Business-Logik!
|
||||
End If
|
||||
```
|
||||
|
||||
### Alle Commands folgen diesem Muster
|
||||
|
||||
| Methode | Aktion |
|
||||
|---------|--------|
|
||||
| `$cEdit` | Bearbeitung starten |
|
||||
| `$cInsert` | Neuen Datensatz anlegen |
|
||||
| `$cSave` | Speichern |
|
||||
| `$cCancel` | Abbrechen |
|
||||
| `$cDelete` | Loeschen |
|
||||
| `$cPrint` | Drucken |
|
||||
| `$cAudit` | Aenderungshistorie anzeigen |
|
||||
| `$cNextVersion` | Naechste Version |
|
||||
| `$cFirst` / `$cLast` | Navigation |
|
||||
|
||||
**Essentielle Erkenntnis**: `wMainWindow` ruft IMMER `$closeRelatedWindow()` VOR der Command-Delegation auf. Das schliesst alle zugehoerigen Popup-Fenster.
|
||||
|
||||
---
|
||||
|
||||
## 4. wSubWindow - Das eingebettete Fenster
|
||||
|
||||
`wSubWindow` erbt ebenfalls von `wBaseWindow` und ist die Superklasse fuer alle **eingebetteten Panels/Tabs**. Es ist der Schluessel zur rekursiven Architektur.
|
||||
|
||||
### Zusaetzliche Instanzvariablen
|
||||
|
||||
| Variable | Typ | Bedeutung |
|
||||
|----------|-----|-----------|
|
||||
| `iiRef2Parent` | Item-Ref | Referenz ZURUECK zum Elternfenster |
|
||||
| `icFormWindow` | Char | Name des Formulars (fuer Rechte-Check) |
|
||||
| `icSubWindow` | Char | Name des SubWindow-Felds |
|
||||
| `iiRef2Window` | Item-Ref | Referenz zum Hauptfenster |
|
||||
|
||||
### Der Lebenszyklus eines SubWindows
|
||||
|
||||
#### Phase 1: $construct - Aufbau
|
||||
|
||||
```omnis
|
||||
## wSubWindow.$construct:
|
||||
Do inherited ## 1. Geerbte Initialisierung
|
||||
Set reference $cinst.iiRef2SubWindow to $cinst.$objs.wSubWindow.$ref ## 2. Eigenes SubWindow finden
|
||||
Set reference $cinst.iiRef2Parent to pRef2Parent ## 3. Rueck-Referenz zum Eltern setzen
|
||||
Do $cinst.$translateObjects($cinst) ## 4. UI uebersetzen
|
||||
Calculate icFormWindow as pFormWiindow ## 5. Formular-Name merken
|
||||
Do iiRef2Parent.$setControl2SubWindow(kTrue) ## 6. KONTROLLE UEBERNEHMEN!
|
||||
Do $cinst.$setRowName('irRow') ## 7. Standard Row-Name
|
||||
Do $cinst.$setListName('ilList') ## 8. Standard List-Name
|
||||
```
|
||||
|
||||
> **Schritt 6 ist ENTSCHEIDEND!** Hier meldet sich das SubWindow beim Elternfenster an: "Ab jetzt gehen alle Commands an MICH!"
|
||||
|
||||
#### Phase 2: Command-Delegation (identisch zum MainWindow)
|
||||
|
||||
```omnis
|
||||
## wSubWindow.$cEdit:
|
||||
If $cinst.ibControl2SubWindow ## Hat DIESES SubWindow ein eigenes SubWindow?
|
||||
Do iiRef2SubWindow.$cEdit ## -> Ja: Weiterleiten!
|
||||
Else
|
||||
Do $cinst.ioCode.$cEdit() ## -> Nein: Eigene Business-Logik!
|
||||
End If
|
||||
```
|
||||
|
||||
#### Phase 3: $destruct - Aufraemen
|
||||
|
||||
```omnis
|
||||
## wSubWindow.$destruct:
|
||||
If iiRef2Parent
|
||||
Do iiRef2Parent.$setControl2SubWindow(kFalse) ## 1. Kontrolle ZURUECKGEBEN!
|
||||
End If
|
||||
Do ilList.$define() ## 2. Liste leeren
|
||||
Do irRow.$define() ## 3. Row leeren
|
||||
Do irRowSearch.$define() ## 4. Such-Row leeren
|
||||
```
|
||||
|
||||
> **WICHTIG**: Ohne diesen $destruct wuerde das Elternfenster denken, das SubWindow existiert noch!
|
||||
|
||||
#### Phase 4: $closeWindow - Fenster schliessen
|
||||
|
||||
```omnis
|
||||
## wSubWindow.$closeWindow:
|
||||
Do iiRef2Parent.$closeWindow() ## Delegiert das Schliessen an das Elternfenster!
|
||||
```
|
||||
|
||||
> SubWindows schliessen sich NICHT selbst - sie bitten den Parent, das ganze Fenster zu schliessen!
|
||||
|
||||
---
|
||||
|
||||
## 5. Die bidirektionale Referenz-Kette
|
||||
|
||||
Das Herzstueck der Architektur ist die **bidirektionale Verkettung**:
|
||||
|
||||
```
|
||||
wMainWindow wSubWindow (Ebene 1) wSubWindow (Ebene 2)
|
||||
+-----------------+ +--------------------+ +------------------+
|
||||
| iiRef2SubWindow | --------> | $cinst | | |
|
||||
| | | iiRef2Parent |----+--------> | (existiert nicht |
|
||||
| ibControl2Sub- | | iiRef2SubWindow|----|--------> | auf dieser Ebene)|
|
||||
| Window = kTrue | | | | |
|
||||
+-----------------+ | ibControl2Sub- | +------------------+
|
||||
| Window = kFalse |
|
||||
+--------------------+
|
||||
|
||||
Aufwaerts: iiRef2Parent (Kind -> Elter)
|
||||
Abwaerts: iiRef2SubWindow (Elter -> Kind)
|
||||
```
|
||||
|
||||
### Wie die Kontrolle fliesst
|
||||
|
||||
1. **User klickt "Edit"** im Hauptfenster
|
||||
2. `wMainWindow.$cEdit` wird aufgerufen
|
||||
3. `ibControl2SubWindow` ist `kTrue` -> an `iiRef2SubWindow.$cEdit` delegieren
|
||||
4. `wSubWindow.$cEdit` (Ebene 1) wird aufgerufen
|
||||
5. `ibControl2SubWindow` auf Ebene 1 ist `kFalse` -> `ioCode.$cEdit()` aufrufen
|
||||
6. Die Business-Logik (`oOrders`) fuehrt die eigentliche Aktion aus
|
||||
|
||||
### Konkretes Beispiel: Sales/wOrders
|
||||
|
||||
```
|
||||
wOrders (wMainWindow)
|
||||
|
|
||||
+-- iiRef2SubWindow -> wOrder (wSubWindow)
|
||||
|
|
||||
+-- iiRef2Parent -> wOrders (zurueck)
|
||||
+-- iiRef2SubWindow -> wOrderItems (wSubWindow)
|
||||
|
|
||||
+-- iiRef2Parent -> wOrder (zurueck)
|
||||
+-- ioCode -> oOrderItems (Business-Logik)
|
||||
```
|
||||
|
||||
**Command-Fluss bei "Edit":**
|
||||
```
|
||||
wOrders.$cEdit
|
||||
-> ibControl2SubWindow = kTrue
|
||||
-> wOrder.$cEdit
|
||||
-> ibControl2SubWindow = kTrue (falls OrderItems aktiv)
|
||||
-> wOrderItems.$cEdit
|
||||
-> ibControl2SubWindow = kFalse
|
||||
-> oOrderItems.$cEdit() <-- HIER wird die Aktion ausgefuehrt!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Das $EnableTabPane-Pattern
|
||||
|
||||
Wenn ein SubWindow in den Edit-Modus wechselt, muessen die Tab-Reiter gesperrt werden - und zwar **aufwaerts in der Hierarchie**!
|
||||
|
||||
### Im SubWindow (z.B. wAdditionalCosts):
|
||||
```omnis
|
||||
## wAdditionalCosts.$EnableTabPane:
|
||||
# Verriegelt Reiter der TabPane gegeneinander
|
||||
# und ruft im Elternfenster die Methode $EnablePane
|
||||
Do iiRef2Parent.$EnableTabPane($cinst.inEDMode)
|
||||
```
|
||||
|
||||
### Im wBaseWindow:
|
||||
```omnis
|
||||
## wBaseWindow.$EnableTabPane:
|
||||
Do $cwind.$objs.TP.$enableTabPanes(pEDMode)
|
||||
```
|
||||
|
||||
**Ablauf:**
|
||||
1. SubWindow wechselt in Edit-Modus (`inEDMode = kEdit`)
|
||||
2. SubWindow ruft `iiRef2Parent.$EnableTabPane(kEdit)`
|
||||
3. Parent sperrt seine TabPane-Reiter (`TP.$enableTabPanes(kEdit)`)
|
||||
4. User kann nicht mehr zwischen Tabs wechseln waehrend er editiert!
|
||||
5. Bei Cancel/Save: `$EnableTabPane(kFalse)` -> Tabs wieder frei
|
||||
|
||||
---
|
||||
|
||||
## 7. Sonderfaelle und Varianten
|
||||
|
||||
### wWebPortal - Kontrolle ablehnen
|
||||
|
||||
```omnis
|
||||
## wWebPortal.$construct:
|
||||
Do inherited
|
||||
Do pRef2Parent.$setControl2SubWindow(kFalse) ## Kontrolle NICHT uebernehmen!
|
||||
Do $cinst.$translateObjects($cinst)
|
||||
```
|
||||
|
||||
> Manche SubWindows wollen die Kontrolle NICHT haben! Sie sind nur Anzeige-Panels.
|
||||
|
||||
### wOrder.$ActiveWindow.$assign - Upstream-Delegation
|
||||
|
||||
```omnis
|
||||
## wOrder (Sales).$ActiveWindow.$assign:
|
||||
Do iiRef2Parent.$ActiveWindow.$assign(pActive) ## CGrid im Elternfenster ausschalten
|
||||
```
|
||||
|
||||
> SubWindows koennen auch beliebige Custom-Methoden an den Parent delegieren!
|
||||
|
||||
### wOrderItems.$CallParentDoCommand - Command nach oben
|
||||
|
||||
```omnis
|
||||
## wOrderItems.$CallParentDoCommand:
|
||||
Do iiRef2Parent.$CallDoCommand
|
||||
```
|
||||
|
||||
> Manchmal muss ein SubWindow den Parent bitten, einen Command auszufuehren.
|
||||
|
||||
### wOrder.$SubWindowSavesAttachments - SubWindow-spezifische Logik
|
||||
|
||||
```omnis
|
||||
## wOrder.$SubWindowSavesAttachments:
|
||||
Switch iiRef2SubWindow.$classname
|
||||
Case 'solution2.wText'
|
||||
Do iiRef2SubWindow.$SaveData()
|
||||
Quit method kTrue
|
||||
Default
|
||||
Quit method kFalse
|
||||
End Switch
|
||||
```
|
||||
|
||||
> Das SubWindow kann abfragen, WELCHES SubWindow gerade aktiv ist und unterschiedlich reagieren!
|
||||
|
||||
---
|
||||
|
||||
## 8. Praxis-Anleitung: Eigenes SubWindow erstellen
|
||||
|
||||
### Schritt 1: Klasse anlegen
|
||||
- Erstelle eine neue Window-Klasse (z.B. `wMeinSubWindow`)
|
||||
- Setze `wSubWindow` als Superklasse (`solution2.wSubWindow`)
|
||||
|
||||
### Schritt 2: SubWindow-Feld einbetten
|
||||
- Fuege in das Elternfenster ein **Subwindow-Feld** ein
|
||||
- Name: `wSubWindow` (Konvention!)
|
||||
- Setze die `$classname`-Property auf deine neue Klasse
|
||||
|
||||
### Schritt 3: Business-Logik-Objekt
|
||||
- Erstelle ein Object (z.B. `oMeinCode`) mit den Commands
|
||||
- Instanziiere es als `ioCode` in deinem SubWindow
|
||||
|
||||
### Schritt 4: $construct ueberschreiben (optional)
|
||||
```omnis
|
||||
## wMeinSubWindow.$construct:
|
||||
Do inherited ## IMMER inherited aufrufen! Setzt die Referenzen!
|
||||
# ... eigene Initialisierung ...
|
||||
```
|
||||
|
||||
### Schritt 5: Commands implementieren
|
||||
Wenn dein SubWindow eigene Commands braucht:
|
||||
```omnis
|
||||
## wMeinSubWindow.$cEdit:
|
||||
Do inherited ## Oder: eigene Logik wenn noetig
|
||||
# ... zusaetzliche Aktionen ...
|
||||
```
|
||||
|
||||
### Schritt 6: $EnableTabPane ueberschreiben (wenn noetig)
|
||||
```omnis
|
||||
## wMeinSubWindow.$EnableTabPane:
|
||||
Do iiRef2Parent.$EnableTabPane($cinst.inEDMode)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Haeufige Fehler und Debugging
|
||||
|
||||
### Fehler 1: "Method not found" bei Command-Aufruf
|
||||
**Ursache**: Das SubWindow hat `ibControl2SubWindow = kTrue`, aber das referenzierte SubWindow existiert nicht mehr.
|
||||
**Loesung**: Pruefe ob `$destruct` korrekt `$setControl2SubWindow(kFalse)` aufruft.
|
||||
|
||||
### Fehler 2: Tabs lassen sich nicht sperren
|
||||
**Ursache**: `$EnableTabPane` ruft den Parent nicht auf.
|
||||
**Loesung**: `Do iiRef2Parent.$EnableTabPane($cinst.inEDMode)` im SubWindow.
|
||||
|
||||
### Fehler 3: Aenderungen gehen verloren beim Tab-Wechsel
|
||||
**Ursache**: Der Tab-Wechsel zerstoert das SubWindow, aber die Daten wurden nicht gespeichert.
|
||||
**Loesung**: Im `$destruct` oder vor dem Tab-Wechsel `$cSave` aufrufen.
|
||||
|
||||
### Fehler 4: Commands landen im falschen Fenster
|
||||
**Ursache**: `ibControl2SubWindow` ist nicht korrekt gesetzt.
|
||||
**Debug**: Pruefe zur Laufzeit: `Calculate lDebug as $cinst.ibControl2SubWindow`
|
||||
|
||||
### Fehler 5: SubWindow erhaelt keine Daten
|
||||
**Ursache**: Die bidirektionale Referenz wurde nicht aufgebaut.
|
||||
**Loesung**: Sicherstellen dass `Do inherited` in `$construct` aufgerufen wird!
|
||||
|
||||
### Fehler 6: "Inherited does nothing"
|
||||
**Ursache**: Die Superklasse ist falsch gesetzt (z.B. `wBaseWindow` statt `wSubWindow`).
|
||||
**Loesung**: Superklasse pruefen: Muss `solution2.wSubWindow` sein (mit Library-Prefix bei cross-library!).
|
||||
|
||||
---
|
||||
|
||||
## 10. Zusammenfassung: Die 7 goldenen Regeln
|
||||
|
||||
1. **`ibControl2SubWindow` bestimmt ALLES** - Es entscheidet wohin Commands gehen
|
||||
2. **$construct baut die Kette auf** - `inherited` IMMER aufrufen!
|
||||
3. **$destruct baut die Kette ab** - Kontrolle zurueckgeben!
|
||||
4. **Commands fliessen ABWAERTS** - MainWindow -> SubWindow -> SubSubWindow
|
||||
5. **TabPane-Sperren fliessen AUFWAERTS** - SubWindow -> Parent -> MainWindow
|
||||
6. **$closeWindow delegiert AUFWAERTS** - SubWindows schliessen sich nie selbst
|
||||
7. **ioCode ist der letzte Stopp** - Wenn kein SubWindow aktiv ist, macht ioCode die Arbeit
|
||||
|
||||
---
|
||||
|
||||
## 11. Referenz: Alle delegierten Commands
|
||||
|
||||
### Auf wMainWindow (mit $closeRelatedWindow vorab):
|
||||
`$cEdit`, `$cCancel`, `$cAudit`, `$cNextVersion`
|
||||
|
||||
### Auf wSubWindow (ohne $closeRelatedWindow):
|
||||
`$cEdit`, `$cCancel`, `$cInsert`, `$cDelete`, `$cSave`, `$cPrint`, `$cAudit`, `$cNextVersion`, `$cFirst`, `$cLast`
|
||||
|
||||
### Gemeinsames Pattern:
|
||||
```omnis
|
||||
If $cinst.ibControl2SubWindow
|
||||
Do iiRef2SubWindow.$cXxx() ## Weiterleiten
|
||||
Else
|
||||
Do $cinst.ioCode.$cXxx() ## Selbst ausfuehren
|
||||
End If
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Cross-Library Verwendung
|
||||
|
||||
SubWindows werden oft **library-uebergreifend** genutzt:
|
||||
|
||||
| Library | Klasse | Superklasse |
|
||||
|---------|--------|-------------|
|
||||
| `Sales` | `wOrder` | `solution2.wSubWindow` |
|
||||
| `Sales` | `wOrderItems` | `solution2.wSubWindow` |
|
||||
| `Manufacturing` | `wResourceBasicData` | `solution2.wSubWindow` |
|
||||
| `Manufacturing` | `wVariantsExclLock` | `solution2.wSubWindow` |
|
||||
| `solution2` | `wReminder` | `wSubWindow` |
|
||||
| `solution2` | `wAdditionalCosts` | `wSubWindow` |
|
||||
| `solution2` | `wRTF` | `wSubWindow` |
|
||||
| `solution2` | `wNotice` | `wSubWindow` |
|
||||
| `solution2` | `wNoticeboard` | `wSubWindow` |
|
||||
|
||||
> **Beachte**: Innerhalb der `solution2`-Library reicht `wSubWindow`. Bei Cross-Library-Vererbung muss `solution2.wSubWindow` angegeben werden!
|
||||
121
edu/content/tutorials/onboarding-path.md
Normale Datei
121
edu/content/tutorials/onboarding-path.md
Normale Datei
|
|
@ -0,0 +1,121 @@
|
|||
# Onboarding-Plan: Vom Programmierer zum Solution2/Omnis-Entwickler
|
||||
|
||||
**Gesamtdauer**: ca. 4-6 Wochen (bei ca. 4h/Tag Lernzeit)
|
||||
**Voraussetzung**: Erfahrung in mindestens einer Programmiersprache
|
||||
|
||||
---
|
||||
|
||||
## Woche 1: Omnis Grundlagen
|
||||
|
||||
### Tag 1-2: Orientierung
|
||||
- [ ] Tutorial 01: Omnis Kickstart durcharbeiten
|
||||
- [ ] Omnis Studio IDE installieren und starten
|
||||
- [ ] Ein leeres Projekt (Library) anlegen
|
||||
- [ ] Flashcards: `deck-commands` - erste 10 Karten lernen
|
||||
- [ ] **Praxis**: Ein einfaches Window mit einem Button erstellen, der "Hallo Welt" anzeigt
|
||||
|
||||
### Tag 3-4: Notation & Variablen
|
||||
- [ ] Tutorial 02: Notation Mastery durcharbeiten
|
||||
- [ ] Cheatsheet "Notation" ausdrucken und neben Monitor haengen
|
||||
- [ ] Flashcards: `deck-notation` - erste 15 Karten
|
||||
- [ ] **Praxis**: Window mit 3 Feldern (Name, Alter, Ergebnis) + Button der eine Berechnung macht
|
||||
|
||||
### Tag 5: Listen - Einfuehrung
|
||||
- [ ] Tutorial 03: Lists & Rows - erste Haelfte
|
||||
- [ ] Flashcards: `deck-lists` - erste 10 Karten
|
||||
- [ ] **Praxis**: Eine Liste mit 5 Produkten erstellen und in einem Grid anzeigen
|
||||
|
||||
---
|
||||
|
||||
## Woche 2: Daten & UI
|
||||
|
||||
### Tag 1-2: Listen Vertiefung
|
||||
- [ ] Tutorial 03: Lists & Rows - zweite Haelfte
|
||||
- [ ] Flashcards: `deck-lists` - alle Karten
|
||||
- [ ] **Praxis**: Produktliste mit Suchen, Sortieren, Hinzufuegen, Loeschen
|
||||
|
||||
### Tag 3-4: SQL & Datenbankzugriff
|
||||
- [ ] Tutorial 04: SQL and Data Access
|
||||
- [ ] Flashcards: `deck-sql`
|
||||
- [ ] PostgreSQL-Zugang zu masterdemo erhalten
|
||||
- [ ] **Praxis**: Daten aus der `contact`-Tabelle laden und in einer Liste anzeigen
|
||||
|
||||
### Tag 5: UI-Design
|
||||
- [ ] Tutorial 05: Windows & UI
|
||||
- [ ] Verschiedene Fenster-Typen ausprobieren (Main, Modal, Sub)
|
||||
- [ ] **Praxis**: Ein Kontakt-Formular mit Validierung bauen
|
||||
|
||||
---
|
||||
|
||||
## Woche 3: Solution2 kennenlernen
|
||||
|
||||
### Tag 1-2: Architektur-Ueberblick
|
||||
- [ ] Tutorial 07: Solution2 Deep Dive
|
||||
- [ ] Cheatsheet "Solution2" studieren
|
||||
- [ ] Flashcards: `deck-solution2`
|
||||
- [ ] Die Library-Struktur in Omnis oeffnen und durchblaettern
|
||||
- [ ] **Praxis**: Die Login-Klassen (wLogin, oLogin) oeffnen und den Code nachvollziehen
|
||||
|
||||
### Tag 3-4: Einen Geschaeftsprozess nachvollziehen
|
||||
- [ ] Den Auftragsprozess verfolgen: orders -> deliverynoteitems -> customerinvoices
|
||||
- [ ] Die Klassen wOrders, oOrders, T_Orders im Code oeffnen
|
||||
- [ ] Die Superklassen-Hierarchie (wMainWindow, oSuperData, T_Super) verstehen
|
||||
- [ ] **Praxis**: Einen bestehenden Auftrag in der UI oeffnen und nachvollziehen welche Methoden aufgerufen werden (Debugger nutzen!)
|
||||
|
||||
### Tag 5: Reports & Druckausgabe
|
||||
- [ ] Report-Klassen (r-Praefix) verstehen
|
||||
- [ ] Einen bestehenden Report (z.B. rInvoice) oeffnen und analysieren
|
||||
- [ ] **Praxis**: Einen einfachen Report fuer die Kontaktliste erstellen
|
||||
|
||||
---
|
||||
|
||||
## Woche 4: Web & Fortgeschrittenes
|
||||
|
||||
### Tag 1-2: Remote Forms (Web-Client)
|
||||
- [ ] Tutorial 06: Remote Forms
|
||||
- [ ] Die MobileWork-Library und jsMainForm analysieren
|
||||
- [ ] Unterschied $event vs $eventclient verstehen
|
||||
- [ ] **Praxis**: Ein einfaches Remote Form mit Dateneingabe erstellen
|
||||
|
||||
### Tag 3-4: Eigenes Feature entwickeln
|
||||
- [ ] Aufgabe: Einen kleinen Erweiterung an Solution2 vornehmen (z.B. ein neues Zusatzfeld)
|
||||
- [ ] Dabei die Solution2-Konventionen beachten (Namenskonventionen, Audit-Spalten, Superklassen)
|
||||
- [ ] Code Review durch erfahrenen Entwickler
|
||||
|
||||
### Tag 5: Wiederholung & Flashcards
|
||||
- [ ] Alle Flashcard-Decks wiederholen (Spaced Repetition!)
|
||||
- [ ] Schwache Bereiche identifizieren und gezielt ueeben
|
||||
- [ ] **Quiz**: Gemischtes Quiz ueber alle Themen
|
||||
|
||||
---
|
||||
|
||||
## Woche 5-6: Vertiefung & Spezialisierung
|
||||
|
||||
### Optionale Module (je nach Projekt):
|
||||
- [ ] E-Rechnung: ZUGFeRD/Factur-X/XRechnung im AdminAccounting-Modul
|
||||
- [ ] Fertigung: Manufacturing-Library und PPS
|
||||
- [ ] Qualitaet: QualityInspection-Modul
|
||||
- [ ] Arbeitszeit: StaffWorkTime-Modul
|
||||
- [ ] Mobile: MobileWork-Library vertiefen
|
||||
|
||||
### Laufend:
|
||||
- [ ] Taeglich 15 Min Flashcards (Spaced Repetition)
|
||||
- [ ] Woechentlich 1 "Code Reading" Session (fremden Solution2-Code lesen und verstehen)
|
||||
- [ ] Bei jedem Problem: Erst MCP-Suche, dann Docs, dann Kollegen fragen
|
||||
|
||||
---
|
||||
|
||||
## Erfolgskriterien: Wann bist du "bereit"?
|
||||
|
||||
| Kompetenz | Selbsteinschaetzung |
|
||||
|-----------|---------------------|
|
||||
| Kann Omnis-Code lesen und verstehen | [ ] 1-5 |
|
||||
| Kann einfache Windows/Forms erstellen | [ ] 1-5 |
|
||||
| Versteht die $-Notation | [ ] 1-5 |
|
||||
| Kann Listen erstellen und manipulieren | [ ] 1-5 |
|
||||
| Kann SQL-Abfragen in Omnis schreiben | [ ] 1-5 |
|
||||
| Kennt die Solution2-Architektur | [ ] 1-5 |
|
||||
| Kann eine kleine Aenderung selbstaendig umsetzen | [ ] 1-5 |
|
||||
| Kann den Debugger effektiv nutzen | [ ] 1-5 |
|
||||
|
||||
**Ziel**: Alle Bereiche mindestens 3 von 5.
|
||||
Laden …
Tabelle hinzufügen
Einen Link hinzufügen
In neuem Issue referenzieren