Files
PolyTraderSharp/docs/LEITFADEN-Avalonia-Portierung.md
T
RichardandClaude Opus 5 bf8048b82a Fruehjahrsputz: WinForms ausgebaut (P11/L5), LicenseLabrador abgeloest (D-6)
Das Repo enthaelt ab jetzt ausschliesslich plattformneutralen Code. Build gruen
(0 Fehler), 476 Tests gruen, Linux-Publish der Avalonia-App verifiziert.
Rueckfallebene ist der Tag winforms-final, der jetzt auch auf dem Server liegt.

WinForms endgueltig ausgebaut (P11/L5):
- PolyTrader.App.csproj aus PolyTraderSharp.sln genommen und geloescht
- Ui/, Models/, Licensing/, services/, Properties/, Resources/icons/,
  Program.cs, favicon.ico entfernt - rund 4.500 LOC generierter Designer-Code
- Root-appsettings.json entfernt: exaktes Duplikat der Avalonia-Kopie, die die
  App tatsaechlich liest. Zwei Dateien mit gleichem Inhalt laufen frueher oder
  spaeter auseinander.
- Kein net10.0-windows mehr im Repo; Build-Warnungen von 23 auf 15 gesunken
  (die CS0169 aus den Designer-Resten sind weg)

PNG-Symbole gerettet statt geloescht:
Die Avalonia-App band sie per ..\..\Resources\*.png aus dem Repo-Root ein und
haette sie mitverloren. Sie liegen jetzt in src/PolyTrader.App.Avalonia/Assets/,
wo sie hingehoeren. Der avares-Pfad "Assets/<datei>.png" bleibt unveraendert -
in der gebauten Assembly nachgeprueft.

LicenseLabrador abgeloest (D-6):
- LicenseLabrador.Client aus lib/nuget entfernt, Quellen-Mapping in
  NuGet.Config auf Deploymentcenter.* reduziert, lib/nuget/README.md neu
- Bewusste Abweichung vom Plan: die Watchdog*-Felder in ServerSettings bleiben.
  D-1 hat sie auf die Deploymentcenter-API umgewidmet statt sie zu ersetzen;
  sie werden aktiv benutzt. Die Planzeile stammte aus der Zeit davor.

Weitere Altlasten:
- agentspace/ (30 Dateien: WinForms-Designer-Patcher, fix_mongo.py nach der
  MySQL-Migration, tmp/test/scratch-Skripte) entfernt
- Ideen-fuer-Mittwoch.txt entfernt - Inhalt ist laengst umgesetzt
- tote .gitignore-Regel fuer agentspace/antigravity/ entfernt
- lokal entfernt (nicht versioniert): MongoDB/-Exporte, data.db, .bak-Datei

Dokumentation nachgezogen:
- ANALYSE-Linux-Portierung.md auf Revision 6, P11/L5 als erledigt
- LEITFADEN: Abschnitt B vollstaendig aufgeloest, A5 prueft ab jetzt gegen
  einen Worktree des Tags statt gegen den Arbeitsbaum
- UMSETZUNGSPLAN-Watchdog-LicenseLabrador-Integration.md als ABGELOEST
  gekennzeichnet, D-6 im Deploymentcenter-Plan auf den Iststand gezogen

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 10:58:43 +02:00

400 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Portierungsleitfaden Avalonia (Fortsetzung)
**Stand:** 13.08.2026 (A1A4 abgehakt, Deploymentcenter-Hinweis) · **Zielgruppe:** KI-Agent, der die UI-Portierung fortsetzt
**Vorgänger-Dokumente:** [`UI-SPEZIFIKATION-WinForms.md`](UI-SPEZIFIKATION-WinForms.md) (wie die alte
Oberfläche aussah), [`ANALYSE-Linux-Portierung.md`](ANALYSE-Linux-Portierung.md) (Gesamtplan)
---
## 0. Was du wissen musst, bevor du irgendetwas anfasst
| Punkt | Wert |
|---|---|
| Projekt | `src/PolyTrader.App.Avalonia` |
| Zielframework | `net10.0` |
| Avalonia | **11.3.19****NICHT auf 12 heben!** LiveCharts2 2.0.5 ist gegen 11 gebaut und wirft unter 12 zur Laufzeit `MissingFieldException: Avalonia.Input.Gestures.PinchEvent`. `Avalonia.Controls.DataGrid` folgt einer eigenen Reihe und steht auf **11.3.13**. |
| Diagramme | LiveCharts2 (`LiveChartsCore.SkiaSharpView.Avalonia` 2.0.5) |
| Alte Oberfläche | Git-Tag **`winforms-final`** — dort steht der komplette WinForms-Code |
| Sprache | Alles auf Deutsch: Beschriftungen, Kommentare, Commit-Nachrichten |
### Der Referenzstand ist eine Tag-Abfrage entfernt
```bash
git show winforms-final:Ui/Views/DashboardView.cs
git show winforms-final:src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.Designer.cs
```
**Nutze das immer**, bevor du eine Ansicht anfasst. Die Spezifikation ist eine Zusammenfassung
der Tag ist die Wahrheit.
---
## 1. Die harten Regeln
### 1.1 Layout ist deklarativ. Immer.
> **Jede View besteht aus `View.axaml` (vollständiges Layout) und `View.axaml.cs` (nur Verdrahtung
> und Datenlogik). Steuerelemente und Layout werden NIEMALS zur Laufzeit im Code erzeugt.**
Das ist Richards ausdrückliche Vorgabe und ersetzt die frühere WinForms-Designer-Regel. Wenn du
etwas Dynamisches brauchst (Menüeinträge, Formularfelder), dann so:
- Das **Datenmodell** liefert eine Liste (`ObservableCollection<T>`)
- Das **XAML** beschreibt über `ItemsControl` + `DataTemplate`, wie ein Element aussieht
Vorbilder im Code: [`Controls/WindowMenuBar.axaml`](../src/PolyTrader.App.Avalonia/Controls/WindowMenuBar.axaml)
und [`Controls/SettingsEditor.axaml`](../src/PolyTrader.App.Avalonia/Controls/SettingsEditor.axaml).
### 1.2 Keine festen Farben
Alle Farben kommen aus den Themen-Ressourcen (`App.axaml`, 18 Token je Variante). Im XAML:
```xml
Foreground="{DynamicResource AppMutedTextBrush}"
```
Im Code (nur wo unvermeidbar):
```csharp
btn.Background = ThemeManager.Brush("AppToggleActiveBrush");
```
**Wichtig:** Im Code gesetzte Farben folgen dem Themenwechsel **nicht von selbst**. Wenn du eine
setzt, hänge dich an `ThemeManager.ThemeChanged` und zeichne dort neu — und melde dich beim
`Closed`-Ereignis wieder ab:
```csharp
void OnTheme() => UpdateFarben();
ThemeManager.ThemeChanged += OnTheme;
Closed += (_, _) => ThemeManager.ThemeChanged -= OnTheme;
```
Verfügbare Token: `AppSurfaceBrush`, `AppSurfaceAltBrush`, `AppCardBrush`, `AppBorderBrush`,
`AppMutedTextBrush`, `AppCaptionTextBrush`, `AppReadOnlyTextBrush`, `AppPositiveBrush`,
`AppNegativeBrush`, `AppWarningBrush`, `AppTradeLossBrush`, `AppTradeSmallWinBrush`,
`AppTradeBigWinBrush`, `AppToggleActiveBrush`, `AppToggleSellOnlyBrush`, `AppToggleInactiveBrush`,
`AppChatUserBrush`, `AppChatAgentBrush`.
**Neuen Token gebraucht?** In `App.axaml` in **beiden** `ResourceDictionary`-Blöcken ergänzen und
den Schlüssel in die Prüfliste in `Program.RunSmokeUi` aufnehmen.
### 1.3 Keine Dialoge für Erfolgsmeldungen
Die WinForms-Fassung bestätigte jedes Speichern mit einer MessageBox. Das ist bewusst abgeschafft:
- **Erfolg/Status** → Statuszeile des Fensters (`lblStatus`, `Border Classes="statusbar"`)
- **Echte Entscheidung** (Löschen bestätigen, Eingabe erfragen) → `DialogWindow.Confirm` / `.Prompt`
- **Fehler, der Handeln erfordert** → `DialogWindow.Info`
### 1.4 Fehlerbehandlung: die Ansicht muss bedienbar bleiben
Datenzugriffe immer in `try/catch`. Bei Fehlern die Liste leeren und die Meldung in die Statuszeile
schreiben — **nie** die Ansicht mit einer Ausnahme aufreißen:
```csharp
try { _rows.Clear(); foreach (var r in repo.GetAll()) _rows.Add(r); }
catch (Exception ex) { Status($"Laden fehlgeschlagen: {ex.Message}"); }
```
### 1.5 Module bleiben frei von Avalonia
Die Modul-Fenster liegen in `Views/Modules/` **in der App**, nicht in den Modulprojekten. Grund:
Der kopflose Linux-Daemon (`--headless`) soll keine GUI-Bibliothek mitschleppen. Siehe
[`Views/Modules/README.md`](../src/PolyTrader.App.Avalonia/Views/Modules/README.md).
**Füge NIEMALS eine Avalonia-Paketreferenz zu einem `PolyTrader.Modules.*`-Projekt hinzu.**
---
## 2. Das Baukastenmuster
Jedes Fenster folgt demselben Aufbau. Kopiere [`Views/JobsWindow.axaml`](../src/PolyTrader.App.Avalonia/Views/JobsWindow.axaml)
als kleinstes Beispiel oder [`Views/Modules/CopyTradingWindow.axaml`](../src/PolyTrader.App.Avalonia/Views/Modules/CopyTradingWindow.axaml)
als größtes.
```xml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:PolyTrader.App.Avalonia.Controls"
xmlns:vm="using:PolyTrader.App.Avalonia.ViewModels"
x:Class="PolyTrader.App.Avalonia.Views.MeinFenster"
Title="Titel aus der Spezifikation"
Width="…" Height="…"> <!-- Maße aus UI-SPEZIFIKATION übernehmen -->
<DockPanel>
<controls:WindowMenuBar Name="menuBar" DockPanel.Dock="Top" />
<Border Classes="toolbar" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Name="btnRefresh" Content="Aktualisieren" />
</StackPanel>
</Border>
<Border Classes="statusbar" DockPanel.Dock="Bottom">
<TextBlock Name="lblStatus" Text="Bereit." />
</Border>
<DataGrid Name="grid" x:DataType="vm:MeineZeile">
<DataGrid.Columns>
<DataGridTextColumn Header="Spalte" Width="120" Binding="{Binding Feld}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Window>
```
Im Code-Behind **zwei Konstruktoren** — der parameterlose wird vom XAML-Lader gebraucht:
```csharp
public MeinFenster() => AvaloniaXamlLoader.Load(this);
public MeinFenster(IModuleUiHost host, /* Abhängigkeiten */) : this()
{
this.FindControl<Controls.WindowMenuBar>("menuBar")!.Attach(host, "meine.view.id", this);
// ItemsSource setzen, Ereignisse verdrahten, Daten laden
}
```
### Stolperfallen, die mich Zeit gekostet haben
| Falle | Lösung |
|---|---|
| `AVLN2100: Cannot parse a compiled binding without an explicit x:DataType` | `x:DataType` an das **DataGrid** (nicht an die Spalten) bzw. an das `DataTemplate`. Bei Bindungen gegen den `DataContext` des Fensters: `x:DataType` ans `<Window>`. |
| `CalendarDatePicker.SelectedDate` | ist `DateTime?`, **nicht** `DateTimeOffset?` |
| Neue Einträge erscheinen nicht im Grid | `ObservableCollection<T>` verwenden. `BindingList<T>` implementiert kein `INotifyCollectionChanged` — Avalonia sieht Ergänzungen nicht. |
| Zeilenfarben | über `DataGrid.LoadingRow` setzen, nicht über Styles. Greift auch bei virtualisierten Zeilen. |
| `TryFindResource` nicht gefunden | `using Avalonia.Controls;` (dort als Erweiterungsmethode definiert) |
| Namenskollision `ClosedTradeRow` | Es gibt bereits `PolyTraderSharp.Models.ClosedTradeRow`. Eigene Anzeigezeilen mit Präfix benennen (`CopyClosedTradeRow`). |
| Datei speichern | `StorageProvider.SaveFilePickerAsync(...)`, dann `picker.Path.LocalPath`**nicht** `TryGetLocalPath()` |
---
## 3. Was noch fehlt — die Aufgabenliste
> **Stand 13.08.2026 (nachgeprüft am Code):** A1A4 sind mit Commit `7f0b05e` **umgesetzt**. Der
> Commit hat dieses Dokument seinerzeit nicht mitgezogen, weshalb die Liste unten zwei Wochen lang
> Arbeit als offen auswies, die längst erledigt war. **Real offen ist nur noch A5.**
>
> Die Beschreibungen von A1A4 bleiben als *Soll-Spezifikation* stehen — sie sind die Vorlage, gegen
> die bei A5 geprüft wird. Was tatsächlich gebaut wurde:
| | Soll | Ist (geprüft) |
|---|---|---|
| **A1** ✅ | Drei Widgets + Modul-KPIs | [`LauncherWindow.axaml`](../src/PolyTrader.App.Avalonia/Views/LauncherWindow.axaml) — „Auffällige Trades (24h)", „Warnungen & Fehler (heute)", „Supervisor-KI", `Modul-KPIs` als `ItemsControl`. Supervisor korrekt über nullables `GetService<ISupervisorReportRepository>()`. |
| **A2** ✅ | Account-Übersicht mit Profil-Schaltfläche | Spalten inkl. „3T Winrate %" / „Overall P/L"; `OnOpenPolymarketProfileClick` nutzt wie empfohlen `TopLevel.Launcher.LaunchUriAsync`, mit Hinweis bei fehlender Wallet-Adresse. |
| **A3** ✅ | „Neu" + „Löschen" für Master-Trader | [`CopyTradingWindow.axaml.cs`](../src/PolyTrader.App.Avalonia/Views/Modules/CopyTradingWindow.axaml.cs) — `DeleteSelectedTrader()` mit `DialogWindow.Confirm` vor `_traderRepo.Delete(id)`. |
| **A4** ✅ | Kontextmenü im Terminal | [`TerminalWindow.axaml`](../src/PolyTrader.App.Avalonia/Views/TerminalWindow.axaml) — `<ContextMenu>` am `ScrollViewer` mit „Kopieren" und „Alles auswählen". |
**Wenn du hier etwas änderst, zieh dieses Dokument im selben Commit mit.** Genau das ist beim
letzten Mal unterblieben.
---
### A1 — Launcher: Live-Überblick ✅ ERLEDIGT (Commit `7f0b05e`)
Die WinForms-Fassung hatte unter den Fenster-Buttons ein `LauncherWidgetsPanel` (205 + 244 LOC) mit
drei Bereichen nebeneinander in einem `TableLayoutPanel`:
| Bereich | Inhalt | Datenquelle |
|---|---|---|
| **Auffällige Trades (24h)** | Grid: Modul, Markt, PnL, PnL % | `ITradeLogRepository`, letzte 30 nach Betrag sortiert |
| **Warnungen & Fehler (heute)** | Grid: Zeit, Level, Nachricht | `Logs/{heute:yyyy-MM-dd}.jsonl` über `LogJson.ParseLine`, max. 200 |
| **Supervisor-KI (letzter Bericht)** | Textfeld | `ISupervisorReportRepository.GetRecent(1)` |
Dazu die **Modul-KPIs** (`UpdateModuleKpis`): je Modul PnL/Winrate aus dem Trade-Log.
**Referenz:** `git show winforms-final:Ui/LauncherWidgetsPanel.cs`
**Achtung:** Der Supervisor-Teil darf nur erscheinen, wenn das Modul geladen ist — nutze
`services.GetService<…>()` (nullable) statt `GetRequiredService`.
### A2 — Launcher: Account-Übersicht ✅ ERLEDIGT (Commit `7f0b05e`)
Grid mit: Account, Module, Polymarket, Wallet (USDC), 3T PnL, 3T Winrate %, Overall P/L.
Die Spalte „Polymarket" war ein **Button**, der das Polymarket-Profil des Kontos im Browser öffnet.
**Referenz:** `git show winforms-final:Ui/LauncherForm.cs``UpdateAccountList()`,
`AccountList_CellContentClick()`
**Plattformhinweis:** Das alte `Process.Start(new ProcessStartInfo { UseShellExecute = true })`
funktioniert auch auf Linux, ist aber unnötig — nimm stattdessen
`TopLevel.GetTopLevel(this)!.Launcher.LaunchUriAsync(new Uri(url))`. Das ist Avalonias
plattformneutraler Weg.
### A3 — Copytrading: „Neu" und „Löschen" für Master-Trader ✅ ERLEDIGT (Commit `7f0b05e`)
Die WinForms-`MasterTradersView` hatte vier Werkzeugleisten-Schaltflächen: Aktualisieren, **Neu**,
Speichern, **Löschen**. Im Avalonia-Fenster sind nur Aktualisieren und Speichern verdrahtet.
**Referenz:** `git show winforms-final:src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.cs`
`AddNew()`, `DeleteCurrent()`
Löschen mit `DialogWindow.Confirm` absichern, danach `ITrackedTraderRepository.Delete(id)` und den
Eintrag aus `CopyTradingState.Traders` entfernen.
### A4 — Terminal: Kontextmenü ✅ ERLEDIGT (Commit `7f0b05e`)
Vorhanden sind Schaltflächen für „Alles kopieren" und „Terminal leeren". Es fehlen die Einträge
**„Kopieren" (nur Auswahl)** und **„Alles auswählen"** als Kontextmenü auf der Log-Ausgabe.
In Avalonia deklarativ über `<ContextMenu>` am Container, gebunden an Befehle im Code-Behind.
### A5 — Durchsehen mit echten Daten ⬅️ **der einzige noch offene Punkt**
Alle Fenster wurden **konstruiert** und die App lief, aber es wurde **nicht jedes Fenster mit
echten Daten durchgeklickt**. Layout-Details siehst du erst im Gebrauch:
- Spaltenbreiten (feste Pixel wurden teils in Sternbreiten übersetzt)
- Umbrüche in Werkzeugleisten bei schmalen Fenstern
- Splitter-Positionen (Copytrading Master-Trader, Supervisor Dossiers)
- Ob die KPI-Kacheln bei acht Stück (Accounting) sinnvoll umbrechen
**Vorgehen:** App starten, jedes Fenster öffnen, mit der alten Oberfläche vergleichen.
> **Seit dem Frühjahrsputz am 22.08.2026** liegt die WinForms-Fassung nicht mehr im
> Arbeitsbaum. Zum Vergleich entweder die Bildbeschreibung in
> [UI-SPEZIFIKATION-WinForms.md](./UI-SPEZIFIKATION-WinForms.md) heranziehen oder den alten
> Stand in einem getrennten Arbeitsbaum auschecken — das Repo bleibt dabei unberührt:
>
> ```bash
> git worktree add ../polytrader-winforms winforms-final
> ```
>
> Danach dort `dotnet build PolyTrader.App.csproj` und starten. Aufräumen mit
> `git worktree remove ../polytrader-winforms`.
### B — Ehemals zurückgestellt · ✅ mit der Deploymentcenter-Integration aufgelöst
> **Stand 22.08.2026:** Dieser Abschnitt ist abgearbeitet. Die Deploymentcenter-Integration
> (D-0 bis D-5) ist code-seitig umgesetzt, und mit dem Frühjahrsputz vom 22.08.2026 ist das
> WinForms-Projekt aus dem Repo entfernt. Die Tabelle bleibt als Verlaufsspur stehen.
| Was | Stand heute |
|---|---|
| **Lizenzdialog** (`LicenseDialog`) | ✅ Entfallen. Das WinForms-Fenster ist mit dem Ausbau gelöscht; die Lizenzeingabe liegt jetzt im Avalonia-Einstellungsfenster (D-2). |
| **Lizenz-Startprüfung** (`LicenseGate`) | ✅ Neu gebaut in [`Licensing/LicenseGate.cs`](../src/PolyTrader.App.Avalonia/Licensing/LicenseGate.cs) gegen das Deploymentcenter (D-2). Der frühere Hinweis „setzt gar keine Lizenz durch" gilt nicht mehr. |
| **Watchdog-Heartbeat** | ✅ `WatchdogHeartbeatService` ist auf die Deploymentcenter-API umgestellt (D-1) — inklusive `version`, `os`, `checks`, `metrics` und `status: "stopped"`. Die `Watchdog*`-Felder in `ServerSettings` wurden dabei **umgewidmet, nicht ersetzt**, und sind weiterhin in Gebrauch. |
| **WinForms-Projekt entfernen** | ✅ Erledigt am 22.08.2026 (P11/L5). `PolyTrader.App.csproj`, `Ui/`, `Models/`, `Licensing/`, `services/`, `Properties/`, `Resources/icons/`, `Program.cs` und `favicon.ico` sind gelöscht, das Projekt ist aus der Solution genommen. Rückfallebene: Git-Tag `winforms-final` (liegt auch auf dem Server). |
### C — Kleinere Folgepunkte (kein Blocker)
| Was | Befund |
|---|---|
| Logging kennt `AppTimeZone` nicht | [`TerminalLogger.cs:23`](../src/PolyTrader.Core/Services/TerminalLogger.cs) stempelt mit `DateTime.Now`, und der Launcher liest die Tagesdatei mit `DateTime.Now`. Schreiber und Leser stimmen also überein — **kein Fehler**. Beide ignorieren aber die in P2 eingeführte konfigurierbare Zeitzone, sodass auf einem UTC-Linuxserver mit Anzeigezone `Europe/Berlin` die Dateigrenzen nicht zur angezeigten Uhrzeit passen. Beim Headless-Schritt (L2) mitbehandeln. |
| Ungenutzte Designer-Felder | ✅ Erledigt. Mit dem WinForms-Ausbau (22.08.2026) sind die `CS0169`-Warnungen verschwunden; der Solution-Build wirft noch **15** Warnungen, alle aus dem Avalonia-Projekt (`CS8618` in Fenster-Konstruktoren, je einmal `CS8848` und `CS8602`). |
---
## 4. Wie du prüfst, ob es funktioniert
**Nach jeder Änderung, ohne Ausnahme:**
```bash
dotnet build PolyTraderSharp.sln -v q --nologo
```
```bash
dotnet test tests/PolyTrader.Tests/PolyTrader.Tests.csproj --nologo -v q
```
**Der wichtigste Test — konstruiert alle Fenster kopflos, ohne die Trading-Dienste zu starten:**
```bash
dotnet run --project src/PolyTrader.App.Avalonia --no-build -- --smoke-ui
```
Erwartete Ausgabe (Stand heute):
```
=== Smoke-UI: Fenster-Konstruktion (Avalonia) ===
[OK] core.dashboard (Dashboard)
[OK] core.settings (Server Settings)
[OK] core.jobs (Server Jobs)
[OK] core.terminal (Terminal / Logs)
[OK] copytrading.main (Copytrading)
[OK] resolutionfarming.main (ResolutionFarming)
[OK] supervisor.main (Supervisor)
[OK] accounting.main (Accounting)
[OK] LauncherWindow konstruiert
[OK] ShutdownConfirmWindow konstruiert
[OK] Einstellungs-Editor: 7 Abschnitte, 17 Felder (…)
[OK] Farbschema „Light": alle 18 Farben vorhanden
[OK] Farbschema „Dark": alle 18 Farben vorhanden
=== Smoke-UI OK ===
```
**Der Smoke-Test startet den Host absichtlich NICHT** — sonst liefe die Trading-Engine gegen die
echten Börsen-Endpunkte. Nicht ändern.
**Linux-Tauglichkeit gegenprüfen** (der Sinn der ganzen Übung):
```bash
dotnet publish src/PolyTrader.App.Avalonia -r linux-x64 --self-contained false -o /tmp/pt
```
---
## 5. Wo was liegt
```
src/PolyTrader.App.Avalonia/
├─ App.axaml Farb-Token (Light/Dark) + projektweite Stile
├─ Program.cs Einstieg; BuildHost() ohne UI-Bezug, --headless, --smoke-ui
├─ Shell/
│ ├─ AvaloniaUiHost.cs Fensterverwaltung (IModuleUiHost)
│ ├─ ThemeManager.cs Farbschema + ThemeChanged
│ ├─ ViewIcons.cs Symbolschlüssel → PNG
│ ├─ CoreViews.cs Registrierung der Core-Fenster
│ └─ ModuleViews.cs Registrierung der Modul-Fenster
├─ Controls/
│ ├─ WindowMenuBar.axaml gemeinsame Fensterleiste (auf JEDEM Fenster)
│ └─ SettingsEditor.axaml Ersatz fürs PropertyGrid
├─ ViewModels/ Anzeigezeilen und Datenmodelle
└─ Views/
├─ *.axaml Core-Fenster
└─ Modules/*.axaml Modul-Fenster
```
### Der `SettingsEditor` — nutze ihn
Du brauchst **nie** ein Einstellungsformular von Hand zu bauen. Ein Aufruf genügt:
```csharp
this.FindControl<SettingsEditor>("editorXyz")!.Show(meinEinstellungsObjekt);
```
Er liest `[Category]`, `[DisplayName]`, `[Description]` und `[Browsable(false)]` vom Modell und
rendert Überschrift + Beschriftung links + Feld rechts + Hinweis darunter. Unterstützte Typen:
`string`, `int`/`long`, `bool`, Enums, Nur-Lese-Eigenschaften.
**Ein neues Feld in den Einstellungen** heißt also: Eigenschaft am Modell ergänzen, Attribute dran,
fertig. Kein UI-Code.
---
## 6. Arbeitsweise
1. **Eine Aufgabe aus Abschnitt 3 nehmen**, nicht mehrere gleichzeitig
2. **`git show winforms-final:<pfad>`** — das Original ansehen, bevor du schreibst
3. Umsetzen nach dem Muster aus Abschnitt 2
4. Build + Tests + `--smoke-ui`
5. Committen mit deutscher Nachricht, die **das Warum** erklärt, nicht nur das Was
6. Commit-Fuß: `Co-Authored-By: <dein Name> <deine Adresse>`
### Was du NICHT tun sollst
- Avalonia auf 12 heben (siehe Abschnitt 0)
- Avalonia in die Modulprojekte ziehen
- Layout im Code aufbauen
- Feste Farben verwenden
- Den Lizenzdialog portieren
- Das WinForms-Projekt löschen
- Den Smoke-Test den Host starten lassen
- Fachlogik in Fenster verlagern — Auswertung gehört in `TradeAnalytics`, `AccountingEngine` usw.
### Wenn du unsicher bist
Der Tag `winforms-final` beantwortet fast jede Frage zum *bisherigen* Verhalten. Wenn er es nicht
tut und die Entscheidung fachlich ist (Handelslogik, Buchhaltung, Steuern), **frag nach**, statt zu
raten. Bei reinen Darstellungsfragen entscheide selbst und schreib eine Zeile ins Commit, warum.