Files
PolyTraderSharp/docs/LEITFADEN-Avalonia-Portierung.md
T
RichardandClaude Opus 5 29ea62fc7f 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>
2026-08-10 08:34:54 +02:00

15 KiB
Raw Blame History

Portierungsleitfaden Avalonia (Fortsetzung)

Stand: 06.08.2026 · Zielgruppe: KI-Agent, der die UI-Portierung fortsetzt Vorgänger-Dokumente: UI-SPEZIFIKATION-WinForms.md (wie die alte Oberfläche aussah), 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.19NICHT 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

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 und Controls/SettingsEditor.axaml.

1.2 Keine festen Farben

Alle Farben kommen aus den Themen-Ressourcen (App.axaml, 18 Token je Variante). Im XAML:

Foreground="{DynamicResource AppMutedTextBrush}"

Im Code (nur wo unvermeidbar):

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:

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 erfordertDialogWindow.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:

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.

Füge NIEMALS eine Avalonia-Paketreferenz zu einem PolyTrader.Modules.*-Projekt hinzu.


2. Das Baukastenmuster

Jedes Fenster folgt demselben Aufbau. Kopiere Views/JobsWindow.axaml als kleinstes Beispiel oder Views/Modules/CopyTradingWindow.axaml als größtes.

<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:

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.LocalPathnicht 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.csUpdateAccountList(), 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.csAddNew(), 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:

dotnet build PolyTraderSharp.sln -v q --nologo
dotnet test tests/PolyTrader.Tests/PolyTrader.Tests.csproj --nologo -v q

Der wichtigste Test — konstruiert alle Fenster kopflos, ohne die Trading-Dienste zu starten:

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):

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:

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.