Files
IBKRTrader/src/IBKRTrader.Core/Ui/WindowManager.cs
T
Richard c20538c8d8 @
R1: Umbau auf Multi-Projekt-Struktur (Vorbild PolytraderSharp)

Kurskorrektur: Vorbild ist PolytraderSharp (C#), nicht die veraltete Python-Version.
Reiner Strukturumbau, Verhalten unveraendert.

- src/IBKRTrader.Core (classlib): Core-Code + UI-Contract (ModuleFormBase, WindowManager)
- src/IBKRTrader.Modules.CongressTrading (classlib, referenziert nur Core)
- Root: IBKRTrader.App (WinExe) referenziert Core + Modul
- tests/IBKRTrader.Tests: Referenzen auf Core + Modul
- Neue .slnx; ungenutztes HtmlAgilityPack entfernt
- docs/ARCHITECTURE.md: korrigierter Ziel-Stand + R-Phasenplan
- Build + 38/38 Tests gruen; App startet

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
2026-07-27 20:26:25 +02:00

83 lines
2.7 KiB
C#

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);
}
}