Modul-Views: parameterloser Ctor + Initialize() (Designer-öffenbar)
Der VS-WinForms-Designer braucht einen parameterlosen Konstruktor, um eine Form zu instanziieren. Die drei Views hatten nur einen DI-Ctor -> Designer hätte sie nicht öffnen können. Umgestellt auf das Core-View-Muster (DashboardView): - Parameterloser Ctor: InitializeComponent() + Event-Wiring. - Initialize(deps): Abhängigkeiten setzen + Daten laden. - Felder nullable + Null-Guards in den Handlern. - CopyTradingModule.RegisterUi: new View() + view.Initialize(...) statt DI-Ctor. Verifiziert: Build grün, --smoke-ui grün (3 Accounts / 32 Trader, alle Views OK). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4caba5bfd1
commit
76007db93f
@@ -62,10 +62,15 @@ namespace PolyTrader.Modules.CopyTrading
|
||||
Title = "Master-Trader",
|
||||
Group = "CopyTrading",
|
||||
Order = 100,
|
||||
CreateForm = () => new MasterTradersView(
|
||||
services.GetRequiredService<ITrackedTraderRepository>(),
|
||||
services.GetRequiredService<TradingState>(),
|
||||
services.GetRequiredService<CopyTradingState>())
|
||||
CreateForm = () =>
|
||||
{
|
||||
var view = new MasterTradersView();
|
||||
view.Initialize(
|
||||
services.GetRequiredService<ITrackedTraderRepository>(),
|
||||
services.GetRequiredService<TradingState>(),
|
||||
services.GetRequiredService<CopyTradingState>());
|
||||
return view;
|
||||
}
|
||||
});
|
||||
|
||||
host.RegisterView(new ModuleView
|
||||
@@ -74,10 +79,15 @@ namespace PolyTrader.Modules.CopyTrading
|
||||
Title = "Geschlossene Copytrades",
|
||||
Group = "CopyTrading",
|
||||
Order = 110,
|
||||
CreateForm = () => new ClosedTradesView(
|
||||
services.GetRequiredService<ICopyTradeLogRepository>(),
|
||||
services.GetRequiredService<TradingState>(),
|
||||
services.GetRequiredService<CopyTradingState>())
|
||||
CreateForm = () =>
|
||||
{
|
||||
var view = new ClosedTradesView();
|
||||
view.Initialize(
|
||||
services.GetRequiredService<ICopyTradeLogRepository>(),
|
||||
services.GetRequiredService<TradingState>(),
|
||||
services.GetRequiredService<CopyTradingState>());
|
||||
return view;
|
||||
}
|
||||
});
|
||||
|
||||
host.RegisterView(new ModuleView
|
||||
@@ -86,10 +96,15 @@ namespace PolyTrader.Modules.CopyTrading
|
||||
Title = "Copytrading-Account-Einstellungen",
|
||||
Group = "CopyTrading",
|
||||
Order = 120,
|
||||
CreateForm = () => new AccountSettingsView(
|
||||
services.GetRequiredService<ICopyTradingAccountSettingsRepository>(),
|
||||
services.GetRequiredService<TradingState>(),
|
||||
services.GetRequiredService<CopyTradingState>())
|
||||
CreateForm = () =>
|
||||
{
|
||||
var view = new AccountSettingsView();
|
||||
view.Initialize(
|
||||
services.GetRequiredService<ICopyTradingAccountSettingsRepository>(),
|
||||
services.GetRequiredService<TradingState>(),
|
||||
services.GetRequiredService<CopyTradingState>());
|
||||
return view;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,28 +15,34 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
/// </summary>
|
||||
public partial class AccountSettingsView : Form
|
||||
{
|
||||
private readonly ICopyTradingAccountSettingsRepository _repo;
|
||||
private readonly TradingState _state;
|
||||
private readonly CopyTradingState _copyState;
|
||||
private ICopyTradingAccountSettingsRepository? _repo;
|
||||
private TradingState? _state;
|
||||
private CopyTradingState? _copyState;
|
||||
|
||||
private CopyTradingAccountSettings? _current;
|
||||
|
||||
public AccountSettingsView(ICopyTradingAccountSettingsRepository repo, TradingState state, CopyTradingState copyState)
|
||||
// Parameterloser Konstruktor für den WinForms-Designer.
|
||||
public AccountSettingsView()
|
||||
{
|
||||
_repo = repo;
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
cmbAccounts.SelectedIndexChanged += (_, _) => LoadSelected();
|
||||
btnSave.Click += (_, _) => Save();
|
||||
}
|
||||
|
||||
/// <summary>Injiziert die Abhängigkeiten (nach der DI-Auflösung) und füllt die Auswahl.</summary>
|
||||
public void Initialize(ICopyTradingAccountSettingsRepository repo, TradingState state, CopyTradingState copyState)
|
||||
{
|
||||
_repo = repo;
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
PopulateAccounts();
|
||||
}
|
||||
|
||||
private void PopulateAccounts()
|
||||
{
|
||||
if (_state == null) return;
|
||||
|
||||
var items = _state.Accounts.Values
|
||||
.OrderBy(a => a.AccountId)
|
||||
.Select(a => new AccountItem(a.AccountId, string.IsNullOrEmpty(a.Name) ? $"#{a.AccountId}" : $"{a.Name} (#{a.AccountId}){(a.IsDemo ? " · Demo" : "")}"))
|
||||
@@ -52,7 +58,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
|
||||
private void LoadSelected()
|
||||
{
|
||||
if (cmbAccounts.SelectedItem is not AccountItem item) return;
|
||||
if (_repo == null || cmbAccounts.SelectedItem is not AccountItem item) return;
|
||||
_current = _repo.Get(item.Id) ?? new CopyTradingAccountSettings { AccountId = item.Id };
|
||||
pgSettings.SelectedObject = _current;
|
||||
lblHint.Text = $"Einstellungen für Account #{item.Id}.";
|
||||
@@ -60,7 +66,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
|
||||
private void Save()
|
||||
{
|
||||
if (_current == null) return;
|
||||
if (_current == null || _repo == null || _copyState == null) return;
|
||||
_repo.Upsert(_current);
|
||||
_copyState.AccountSettings[_current.AccountId] = _current;
|
||||
lblHint.Text = $"Gespeichert für Account #{_current.AccountId} um {DateTime.Now:HH:mm:ss}.";
|
||||
|
||||
@@ -15,16 +15,13 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
/// </summary>
|
||||
public partial class ClosedTradesView : Form
|
||||
{
|
||||
private readonly ICopyTradeLogRepository _tradeLog;
|
||||
private readonly TradingState _state;
|
||||
private readonly CopyTradingState _copyState;
|
||||
private ICopyTradeLogRepository? _tradeLog;
|
||||
private TradingState? _state;
|
||||
private CopyTradingState? _copyState;
|
||||
|
||||
public ClosedTradesView(ICopyTradeLogRepository tradeLog, TradingState state, CopyTradingState copyState)
|
||||
// Parameterloser Konstruktor für den WinForms-Designer.
|
||||
public ClosedTradesView()
|
||||
{
|
||||
_tradeLog = tradeLog;
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
colEntry.DefaultCellStyle.Format = "F3";
|
||||
@@ -36,12 +33,21 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
colClosedAt.DefaultCellStyle.Format = "dd.MM.yyyy HH:mm";
|
||||
|
||||
btnRefresh.Click += (_, _) => LoadData();
|
||||
}
|
||||
|
||||
/// <summary>Injiziert die Abhängigkeiten (nach der DI-Auflösung) und lädt die Daten.</summary>
|
||||
public void Initialize(ICopyTradeLogRepository tradeLog, TradingState state, CopyTradingState copyState)
|
||||
{
|
||||
_tradeLog = tradeLog;
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void LoadData()
|
||||
{
|
||||
if (_tradeLog == null) return;
|
||||
|
||||
var rows = _tradeLog.Find(_ => true)
|
||||
.OrderByDescending(t => t.ClosedAt)
|
||||
.Select(t => new ClosedTradeRow
|
||||
@@ -80,14 +86,14 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
private string ResolveAccount(int accountId, bool isDemo)
|
||||
{
|
||||
string suffix = isDemo ? " (Demo)" : "";
|
||||
if (_state.Accounts.TryGetValue(accountId, out var acc) && !string.IsNullOrEmpty(acc.Name))
|
||||
if (_state != null && _state.Accounts.TryGetValue(accountId, out var acc) && !string.IsNullOrEmpty(acc.Name))
|
||||
return acc.Name + suffix;
|
||||
return $"#{accountId}{suffix}";
|
||||
}
|
||||
|
||||
private string ResolveTrader(int traderId)
|
||||
{
|
||||
if (_copyState.Traders.TryGetValue(traderId, out var t) && !string.IsNullOrEmpty(t.DisplayName))
|
||||
if (_copyState != null && _copyState.Traders.TryGetValue(traderId, out var t) && !string.IsNullOrEmpty(t.DisplayName))
|
||||
return t.DisplayName;
|
||||
return traderId > 0 ? $"#{traderId}" : "Unbekannt";
|
||||
}
|
||||
|
||||
@@ -17,19 +17,16 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
/// </summary>
|
||||
public partial class MasterTradersView : Form
|
||||
{
|
||||
private readonly ITrackedTraderRepository _repo;
|
||||
private readonly TradingState _state;
|
||||
private readonly CopyTradingState _copyState;
|
||||
private ITrackedTraderRepository? _repo;
|
||||
private TradingState? _state;
|
||||
private CopyTradingState? _copyState;
|
||||
|
||||
private BindingList<TrackedTrader> _binding = new();
|
||||
private TrackedTrader? _current;
|
||||
|
||||
public MasterTradersView(ITrackedTraderRepository repo, TradingState state, CopyTradingState copyState)
|
||||
// Parameterloser Konstruktor für den WinForms-Designer.
|
||||
public MasterTradersView()
|
||||
{
|
||||
_repo = repo;
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
colWinrate.DefaultCellStyle.Format = "F1";
|
||||
@@ -40,12 +37,21 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
tsDelete.Click += (_, _) => DeleteCurrent();
|
||||
tsRefresh.Click += (_, _) => LoadData();
|
||||
grid.SelectionChanged += (_, _) => OnSelectionChanged();
|
||||
}
|
||||
|
||||
/// <summary>Injiziert die Abhängigkeiten (nach der DI-Auflösung) und lädt die Trader.</summary>
|
||||
public void Initialize(ITrackedTraderRepository repo, TradingState state, CopyTradingState copyState)
|
||||
{
|
||||
_repo = repo;
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void LoadData()
|
||||
{
|
||||
if (_repo == null) return;
|
||||
|
||||
var traders = _repo.GetAll().OrderBy(t => t.Id).ToList();
|
||||
_binding = new BindingList<TrackedTrader>(traders);
|
||||
grid.DataSource = _binding;
|
||||
@@ -68,6 +74,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
pgDetail.SelectedObject = trader;
|
||||
|
||||
clbAccounts.Items.Clear();
|
||||
if (_state == null) return;
|
||||
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" : "")}";
|
||||
@@ -94,7 +101,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
|
||||
private void SaveCurrent()
|
||||
{
|
||||
if (_current == null) { lblHint.Text = "Kein Trader ausgewählt."; return; }
|
||||
if (_current == null || _repo == null || _copyState == null) { lblHint.Text = "Kein Trader ausgewählt."; return; }
|
||||
|
||||
_current.AssignedAccountIds = clbAccounts.CheckedItems.Cast<AccountItem>().Select(a => a.Id).ToHashSet();
|
||||
|
||||
@@ -106,7 +113,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
|
||||
private void DeleteCurrent()
|
||||
{
|
||||
if (_current == null) { lblHint.Text = "Kein Trader ausgewählt."; return; }
|
||||
if (_current == null || _repo == null || _copyState == 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)
|
||||
|
||||
Reference in New Issue
Block a user