@
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:
@@ -0,0 +1,25 @@
|
||||
namespace IBKRTrader.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Basisklasse für eigenständige Modul-Fenster.
|
||||
/// Stellt gemeinsame Grundeinstellungen (Größe, Startposition) bereit und
|
||||
/// hält den Modul-Key, damit der Launcher/WindowManager das Fenster zuordnen kann.
|
||||
/// </summary>
|
||||
public class ModuleFormBase : Form
|
||||
{
|
||||
/// <summary>Kürzel des zugehörigen Moduls (z. B. "CT").</summary>
|
||||
public string ModuleKey { get; }
|
||||
|
||||
// Parameterloser Ctor nur für den WinForms-Designer.
|
||||
protected ModuleFormBase() : this("") { }
|
||||
|
||||
protected ModuleFormBase(string moduleKey)
|
||||
{
|
||||
ModuleKey = moduleKey;
|
||||
Width = 900;
|
||||
Height = 600;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
ShowInTaskbar = true;
|
||||
MinimumSize = new Size(600, 400);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
namespace IBKRTrader.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Verwaltet die eigenständigen Modul-Fenster des Launchers.
|
||||
/// Pro Modul-Key wird höchstens ein Fenster gehalten: erneutes Öffnen
|
||||
/// fokussiert das bestehende Fenster statt ein zweites zu erzeugen.
|
||||
///
|
||||
/// Die tatsächlichen UI-Aktionen (Show/Focus) sind in überschreibbare
|
||||
/// Methoden gekapselt, damit die Tracking-Logik ohne Message-Loop testbar ist.
|
||||
/// </summary>
|
||||
public class WindowManager
|
||||
{
|
||||
private readonly Dictionary<string, Form> _open = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>Öffnet das Fenster zum Key oder fokussiert ein bereits offenes.</summary>
|
||||
public Form OpenOrFocus(string key, Func<Form> factory)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(key);
|
||||
ArgumentNullException.ThrowIfNull(factory);
|
||||
|
||||
if (_open.TryGetValue(key, out var existing))
|
||||
{
|
||||
if (!existing.IsDisposed)
|
||||
{
|
||||
Focus(existing);
|
||||
return existing;
|
||||
}
|
||||
_open.Remove(key);
|
||||
}
|
||||
|
||||
var form = factory();
|
||||
_open[key] = form;
|
||||
form.FormClosed += OnFormClosed;
|
||||
Present(form);
|
||||
return form;
|
||||
}
|
||||
|
||||
/// <summary>True, wenn zum Key aktuell ein nicht-disposetes Fenster offen ist.</summary>
|
||||
public bool IsOpen(string key)
|
||||
=> _open.TryGetValue(key, out var f) && !f.IsDisposed;
|
||||
|
||||
/// <summary>Anzahl aktuell offener Fenster.</summary>
|
||||
public int OpenCount => _open.Values.Count(f => !f.IsDisposed);
|
||||
|
||||
/// <summary>Schließt alle offenen Fenster (z. B. beim Beenden des Launchers).</summary>
|
||||
public void CloseAll()
|
||||
{
|
||||
foreach (var f in _open.Values.ToList())
|
||||
{
|
||||
if (!f.IsDisposed)
|
||||
{
|
||||
f.FormClosed -= OnFormClosed;
|
||||
f.Close();
|
||||
}
|
||||
}
|
||||
_open.Clear();
|
||||
}
|
||||
|
||||
// ─── Überschreibbare UI-Aktionen (für Tests) ──────────────────────────────
|
||||
|
||||
/// <summary>Zeigt ein neu erzeugtes Fenster an.</summary>
|
||||
protected virtual void Present(Form form) => form.Show();
|
||||
|
||||
/// <summary>Bringt ein bestehendes Fenster in den Vordergrund.</summary>
|
||||
protected virtual void Focus(Form form)
|
||||
{
|
||||
if (form.WindowState == FormWindowState.Minimized)
|
||||
form.WindowState = FormWindowState.Normal;
|
||||
form.Activate();
|
||||
form.BringToFront();
|
||||
}
|
||||
|
||||
private void OnFormClosed(object? sender, FormClosedEventArgs e)
|
||||
{
|
||||
if (sender is not Form f) return;
|
||||
f.FormClosed -= OnFormClosed;
|
||||
|
||||
var entry = _open.FirstOrDefault(p => ReferenceEquals(p.Value, f));
|
||||
if (entry.Key is not null)
|
||||
_open.Remove(entry.Key);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user