Der Status des Projekts stand verstreut in elf Umsetzungsplaenen, drei Konzepten, der Linux-Analyse und dem Projektstand - teils widersprechend, teils wochenlang veraltet. Ab jetzt gibt es genau eine Statusquelle. docs/ROADMAP.md (neu): - Alle Vorhaben in vier Stufen A bis D, plus technische Schuld und Verlauf. Die Stufen sind eine Reihenfolge, keine Termine: jede schafft die Voraussetzung fuer die naechste. - Statuszeichen: erledigt / offen / blockiert (mit Ursache) / bewusst zurueckgestellt / Idee, nicht beschlossen. Damit ist das, was wir NICHT bauen wollen, sichtbar vorgehalten statt unauffindbar in einem Plan zu schlummern. - Inhaltlich getragen, nicht nur verlinkt: je Vorhaben Ziel, Phasen, Akzeptanzkriterien, offene Entscheidungen und Leitplanken aus den Quelldokumenten. - Sichtbar gemacht, was vorher zwischen den Dokumenten verborgen lag: CopyTrading Phase 1 ist der Engpass der gesamten Roadmap (MarketMaking und BundleArbitrage haben harte Voraussetzungen darauf), und die Sniper-Metriken aus Phase 3.2 sind ein Spezialfall des StrategieDrift-Fingerprints - zusammen bauen statt doppelt. Archiv (docs/archiv/): - 15 Dokumente verschoben (11 Umsetzungsplaene, 3 Konzepte, ANALYSE-Linux-Portierung). Sie bleiben die Bauanleitungen mit Code-Bezuegen, Risikotabellen und Begruendungen - eingefroren ist nur ihr Status. - archiv/README.md ordnet jedes Dokument seinem Roadmap-Punkt zu. Verweise nachgezogen - der eigentliche Aufwand: - 25 Markdown-Links repariert. 15 davon verschiebungsbedingt (eine Ebene tiefer), der Rest war schon vorher falsch: die Ideensammlung verlinkte Quellcode relativ zum Repo-Wurzelverzeichnis statt zu docs/. - 12 Dateien ausserhalb von docs/ verwiesen in Kommentaren auf die Plaene (csproj, props, setup.json, sechs Quelldateien) - alle auf archiv/ umgebogen. - Verweise auf Dateien, die der Fruehjahrsputz geloescht hat (Ui/, Program.cs, WindowMenuBar), zu Klartext entschaerft statt tote Links zu lassen. - Gegenprobe: 85 Links geprueft, 0 kaputt. Build gruen, 476 Tests gruen. PROJEKTSTAND.md entdoppelt: Abschnitt "Offen" verweist jetzt auf die Roadmap. Arbeitsteilung ist damit klar - Projektstand sagt was IST, Roadmap was KOMMT. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
400 lines
19 KiB
Markdown
400 lines
19 KiB
Markdown
# Portierungsleitfaden Avalonia (Fortsetzung)
|
||
|
||
**Stand:** 13.08.2026 (A1–A4 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`](./archiv/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` (am 22.08.2026 entfernt, siehe Tag `winforms-final`)
|
||
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):** A1–A4 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 A1–A4 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.
|