Files
PolyTraderSharp/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.cs
T
RichardandClaude Opus 4.8 4caba5bfd1 Copytrading-Views auf Designer-Muster umgestellt
Die drei Modul-Views wurden von komplett code-first (alle Controls im Konstruktor)
auf das Standard-WinForms-Designer-Muster umgestellt (je X.cs + X.Designer.cs mit
InitializeComponent), damit sie im Designer geöffnet und bearbeitet werden können:

- ClosedTradesView, AccountSettingsView, MasterTradersView: alle wichtigen
  Steuerelemente (Panels, DataGridView mit expliziten Spalten via
  AutoGenerateColumns=false, PropertyGrid, ToolStrip, GroupBox, CheckedListBox,
  ComboBox, Buttons, Labels, Splitter) sind jetzt Designer-Felder.
- Konsistent mit den Core-Views (DashboardView-Muster): Layout im Designer,
  Zellformate/Event-Wiring/Datenbindung im Code.

Verifiziert: --smoke-ui grün (3 Accounts / 32 Trader; alle Views + Launcher OK).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:11:30 +02:00

128 lines
4.7 KiB
C#
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.
using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using PolyTrader.Modules.CopyTrading.Persistence;
using PolyTraderSharp;
using PolyTraderSharp.Models;
namespace PolyTrader.Modules.CopyTrading.Ui
{
/// <summary>
/// Verwaltung der Master-Trader (mod_copytrading_traders): Liste, Detail-Editor
/// (PropertyGrid) und Account-Zuweisung (welche Accounts diesen Trader kopieren).
/// Persistiert über das Repo und hält den Hot-Path-State
/// (<see cref="CopyTradingState.Traders"/>) synchron.
/// Layout im Designer (MasterTradersView.Designer.cs), Daten/Logik hier.
/// </summary>
public partial class MasterTradersView : Form
{
private readonly ITrackedTraderRepository _repo;
private readonly TradingState _state;
private readonly CopyTradingState _copyState;
private BindingList<TrackedTrader> _binding = new();
private TrackedTrader? _current;
public MasterTradersView(ITrackedTraderRepository repo, TradingState state, CopyTradingState copyState)
{
_repo = repo;
_state = state;
_copyState = copyState;
InitializeComponent();
colWinrate.DefaultCellStyle.Format = "F1";
colPnl.DefaultCellStyle.Format = "F2";
tsNew.Click += (_, _) => AddNew();
tsSave.Click += (_, _) => SaveCurrent();
tsDelete.Click += (_, _) => DeleteCurrent();
tsRefresh.Click += (_, _) => LoadData();
grid.SelectionChanged += (_, _) => OnSelectionChanged();
LoadData();
}
private void LoadData()
{
var traders = _repo.GetAll().OrderBy(t => t.Id).ToList();
_binding = new BindingList<TrackedTrader>(traders);
grid.DataSource = _binding;
if (traders.Count > 0)
grid.CurrentCell = grid.Rows[0].Cells[0];
else
ClearDetail();
lblHint.Text = $"{traders.Count} Master-Trader geladen.";
}
private void OnSelectionChanged()
{
if (grid.CurrentRow?.DataBoundItem is TrackedTrader t)
BindDetail(t);
}
private void BindDetail(TrackedTrader trader)
{
_current = trader;
pgDetail.SelectedObject = trader;
clbAccounts.Items.Clear();
foreach (var acc in _state.Accounts.Values.OrderBy(a => a.AccountId))
{
string label = string.IsNullOrEmpty(acc.Name) ? $"#{acc.AccountId}" : $"{acc.Name} (#{acc.AccountId}){(acc.IsDemo ? " · Demo" : "")}";
int idx = clbAccounts.Items.Add(new AccountItem(acc.AccountId, label));
clbAccounts.SetItemChecked(idx, trader.AssignedAccountIds.Contains(acc.AccountId));
}
}
private void ClearDetail()
{
_current = null;
pgDetail.SelectedObject = null;
clbAccounts.Items.Clear();
}
private void AddNew()
{
int nextId = _binding.Count > 0 ? _binding.Max(t => t.Id) + 1 : 1;
var trader = new TrackedTrader { Id = nextId, DisplayName = $"Neuer Trader {nextId}" };
_binding.Add(trader);
grid.CurrentCell = grid.Rows[_binding.Count - 1].Cells[0];
lblHint.Text = $"Neuer Master-Trader #{nextId} Felder ausfüllen und Speichern.";
}
private void SaveCurrent()
{
if (_current == null) { lblHint.Text = "Kein Trader ausgewählt."; return; }
_current.AssignedAccountIds = clbAccounts.CheckedItems.Cast<AccountItem>().Select(a => a.Id).ToHashSet();
_repo.Upsert(_current);
_copyState.Traders[_current.Id] = _current;
grid.Refresh();
lblHint.Text = $"Gespeichert: #{_current.Id} {_current.DisplayName} ({_current.AssignedAccountIds.Count} Account(s)) um {DateTime.Now:HH:mm:ss}.";
}
private void DeleteCurrent()
{
if (_current == null) { lblHint.Text = "Kein Trader ausgewählt."; return; }
var id = _current.Id;
if (MessageBox.Show($"Master-Trader #{id} ({_current.DisplayName}) wirklich löschen?",
"Löschen bestätigen", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes)
return;
_repo.Delete(id);
_copyState.Traders.TryRemove(id, out _);
_binding.Remove(_current);
ClearDetail();
lblHint.Text = $"Master-Trader #{id} gelöscht.";
}
private sealed record AccountItem(int Id, string Label)
{
public override string ToString() => Label;
}
}
}