Portierungsleitfaden fuer die Fortsetzung der Avalonia-Umstellung
docs/LEITFADEN-Avalonia-Portierung.md: Anleitung fuer einen KI-Agenten, der die UI- Portierung weiterfuehrt. Enthaelt bewusst eine EHRLICHE Luecken-Liste - alle acht Fenster konstruieren zwar, mehrere sind aber inhaltlich unvollstaendig. Gegen den Tag winforms-final verglichen und belegt: - A1: Launcher-Live-Ueberblick fehlt KOMPLETT (auffaellige Trades 24h, Warnungen/Fehler heute aus der JSONL, letzter Supervisor-Bericht, Modul-KPIs) - groesste Luecke - A2: Launcher-Account-Uebersicht fehlt KOMPLETT (inkl. Polymarket-Button je Konto) - A3: Copytrading, Master-Trader: 'Neu' und 'Loeschen' nicht verdrahtet - A4: Terminal-Kontextmenue (Kopieren/Alles auswaehlen) fehlt - A5: kein Fenster wurde mit echten Daten durchgeklickt - Spaltenbreiten, Umbrueche und Splitter-Positionen sind ungeprueft Dazu die Regeln (Layout deklarativ, keine festen Farben, keine Erfolgs-Dialoge, Module bleiben frei von Avalonia), das Baukastenmuster, die Stolperfallen die mich Zeit gekostet haben (x:DataType, CalendarDatePicker-Typ, BindingList vs ObservableCollection, LoadingRow, Namenskollision ClosedTradeRow, SaveFilePicker-Pfad), die Pruefbefehle mit erwarteter Ausgabe und was NICHT angefasst werden darf (Avalonia 12, Lizenzdialog, WinForms-Projekt). Alle Dateiverweise und der dokumentierte Smoke-Befehl sind verifiziert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,359 @@
|
|||||||
|
# Portierungsleitfaden Avalonia (Fortsetzung)
|
||||||
|
|
||||||
|
**Stand:** 06.08.2026 · **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
|
||||||
|
|
||||||
|
Der Stand ist ehrlich gesagt: **alle acht Fenster existieren und konstruieren, aber mehrere sind
|
||||||
|
inhaltlich unvollständig.** Ich habe sie gegen den Tag `winforms-final` verglichen. Sortiert nach
|
||||||
|
Wichtigkeit:
|
||||||
|
|
||||||
|
### A1 — Launcher: Live-Überblick fehlt vollständig ⚠️ größte Lücke
|
||||||
|
|
||||||
|
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 fehlt vollständig ⚠️
|
||||||
|
|
||||||
|
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 fehlen
|
||||||
|
|
||||||
|
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ü fehlt
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Ich habe alle Fenster **konstruiert** und die App laufen lassen, aber **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 `winforms-final`-Screenshots vergleichen.
|
||||||
|
|
||||||
|
### B — Bewusst zurückgestellt (NICHT anfassen)
|
||||||
|
|
||||||
|
| Was | Warum |
|
||||||
|
|---|---|
|
||||||
|
| **Lizenzdialog** (`LicenseDialog`) | Hängt am alten LicenseLabrador-SDK, das durch die Deploymentcenter-Anbindung ersetzt wird. Eine Portierung wäre Wegwerfarbeit. Die Schaltfläche „Lizenz prüfen / setzen …" fehlt deshalb im Einstellungsfenster. |
|
||||||
|
| **WinForms-Projekt entfernen** | Erst wenn die Avalonia-Fassung abgenommen ist. Betrifft `PolyTrader.App` und `Ui/`. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
Reference in New Issue
Block a user