@
Phase 2: Launcher-UI + eigenstaendige Modul-Fenster - Form1 -> LauncherForm (Dateien via git mv, Designer/resx angepasst) - Modul-Tab: Karten aus ModuleRegistry mit "Fenster oeffnen"-Button je Modul - UI/WindowManager: Fenster-Tracking (Key->Form), Re-Open fokussiert, CloseAll - UI/ModuleFormBase: Basisklasse fuer eigenstaendige Modul-Fenster - CongressTradingForm: DB-Kennzahlen + manueller Scrape-Trigger (Phase-4-Ausbau folgt) - WindowManager in DI; Launcher schliesst Modul-Fenster beim Beenden - Tests: WindowManager (6) -> 21/21 gruen; Launcher-Start verifiziert Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
using System.Drawing;
|
||||
using IBKRTrader.Core.Logging;
|
||||
using IBKRTrader.Core.Modules;
|
||||
using IBKRTrader.Core.Workers;
|
||||
using IBKRTrader.Modules.CongressTrading.Database;
|
||||
using IBKRTrader.Modules.CongressTrading.Scraper;
|
||||
using IBKRTrader.Modules.CongressTrading.UI;
|
||||
using IBKRTrader.Modules.CongressTrading.Workers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@@ -58,24 +59,10 @@ public sealed class CongressTradingModule : IModule
|
||||
provider.GetRequiredService<CongressScrapeWorker>()
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Erzeugt das Modul-Fenster. Platzhalter – die vollständige UI folgt in Phase 2.
|
||||
/// </summary>
|
||||
/// <summary>Erzeugt das eigenständige Modul-Fenster.</summary>
|
||||
public Form CreateWindow(IServiceProvider provider)
|
||||
{
|
||||
var form = new Form
|
||||
{
|
||||
Text = $"{DisplayName} ({Key})",
|
||||
Width = 800,
|
||||
Height = 500,
|
||||
StartPosition = FormStartPosition.CenterParent
|
||||
};
|
||||
form.Controls.Add(new Label
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
TextAlign = ContentAlignment.MiddleCenter,
|
||||
Text = $"{DisplayName}\n\nModul-UI folgt in Phase 2."
|
||||
});
|
||||
return form;
|
||||
}
|
||||
=> new CongressTradingForm(
|
||||
provider.GetRequiredService<CongressRepository>(),
|
||||
provider.GetRequiredService<WorkerEngine>(),
|
||||
provider.GetRequiredService<LoggingService>());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
using IBKRTrader.Core.Logging;
|
||||
using IBKRTrader.Core.Workers;
|
||||
using IBKRTrader.Modules.CongressTrading.Database;
|
||||
using IBKRTrader.UI;
|
||||
|
||||
namespace IBKRTrader.Modules.CongressTrading.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Eigenständiges Fenster des CongressTrading-Moduls.
|
||||
/// Phase 2: Grundgerüst mit DB-Kennzahlen und manuellem Scrape-Trigger.
|
||||
/// Die vollständige Trade-/Positions-Ansicht folgt in Phase 4.
|
||||
/// </summary>
|
||||
public sealed class CongressTradingForm : ModuleFormBase
|
||||
{
|
||||
private const string ScrapeWorkerName = "CT-ScrapeWorker";
|
||||
|
||||
private readonly CongressRepository _repo;
|
||||
private readonly WorkerEngine _engine;
|
||||
private readonly LoggingService _logger;
|
||||
|
||||
private readonly Label _lblTrades = new() { AutoSize = true, Location = new Point(20, 70) };
|
||||
private readonly Label _lblMembers = new() { AutoSize = true, Location = new Point(20, 100) };
|
||||
private readonly Button _btnRefresh = new() { Text = "Aktualisieren", Location = new Point(20, 140), Width = 140 };
|
||||
private readonly Button _btnScrape = new() { Text = "Scrape jetzt", Location = new Point(170, 140), Width = 140 };
|
||||
private readonly Label _lblStatus = new() { AutoSize = true, Location = new Point(20, 185), ForeColor = SystemColors.GrayText };
|
||||
|
||||
public CongressTradingForm(CongressRepository repo, WorkerEngine engine, LoggingService logger)
|
||||
: base(CongressTradingModule.ModuleKey)
|
||||
{
|
||||
_repo = repo;
|
||||
_engine = engine;
|
||||
_logger = logger;
|
||||
|
||||
Text = "Congress Trading [CT]";
|
||||
BuildLayout();
|
||||
}
|
||||
|
||||
private void BuildLayout()
|
||||
{
|
||||
var title = new Label
|
||||
{
|
||||
Text = "Congress Trading",
|
||||
Font = new Font(Font.FontFamily, 14f, FontStyle.Bold),
|
||||
Location = new Point(18, 20),
|
||||
AutoSize = true
|
||||
};
|
||||
|
||||
var hint = new Label
|
||||
{
|
||||
Text = "Vollständige Trade- und Positions-Ansicht folgt in Phase 4.",
|
||||
Location = new Point(20, 230),
|
||||
AutoSize = true,
|
||||
ForeColor = SystemColors.GrayText
|
||||
};
|
||||
|
||||
_btnRefresh.Click += async (_, _) => await RefreshStatsAsync();
|
||||
_btnScrape.Click += async (_, _) => await TriggerScrapeAsync();
|
||||
|
||||
Controls.Add(title);
|
||||
Controls.Add(_lblTrades);
|
||||
Controls.Add(_lblMembers);
|
||||
Controls.Add(_btnRefresh);
|
||||
Controls.Add(_btnScrape);
|
||||
Controls.Add(_lblStatus);
|
||||
Controls.Add(hint);
|
||||
}
|
||||
|
||||
protected override async void OnShown(EventArgs e)
|
||||
{
|
||||
base.OnShown(e);
|
||||
await RefreshStatsAsync();
|
||||
}
|
||||
|
||||
private async Task RefreshStatsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var trades = await _repo.GetTradeCountAsync();
|
||||
var members = await _repo.GetMemberCountAsync();
|
||||
_lblTrades.Text = $"Trades in DB: {trades:N0}";
|
||||
_lblMembers.Text = $"Mitglieder in DB: {members:N0}";
|
||||
_lblStatus.Text = $"Aktualisiert: {DateTime.Now:HH:mm:ss}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_lblTrades.Text = "Trades in DB: n/v";
|
||||
_lblMembers.Text = "Mitglieder in DB: n/v";
|
||||
_lblStatus.Text = $"DB nicht erreichbar: {ex.Message}";
|
||||
_logger.Warn(CongressTradingModule.ModuleKey, $"Kennzahlen konnten nicht geladen werden: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TriggerScrapeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_lblStatus.Text = "Scrape angestoßen...";
|
||||
await _engine.TriggerWorkerAsync(ScrapeWorkerName);
|
||||
_logger.Info(CongressTradingModule.ModuleKey, "Scrape-Worker manuell ausgelöst (aus Modul-Fenster).");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_lblStatus.Text = $"Scrape fehlgeschlagen: {ex.Message}";
|
||||
_logger.Error(CongressTradingModule.ModuleKey, "Manueller Scrape-Trigger fehlgeschlagen.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user