using IBKRTrader.Core.Modularity; namespace IBKRTrader.UI; /// /// Verwaltet die registrierten Fenster-Views: öffnet sie als eigenständige Forms, hält je View /// höchstens eine Instanz offen und holt ein bereits offenes Fenster wieder in den Vordergrund. /// Meldet Änderungen am Offen-Status (für die Button-Markierung im Launcher). /// Vorbild: PolytraderSharp ShellUiHost. /// public sealed class ShellUiHost : IModuleUiHost { private readonly List _views = new(); private readonly Dictionary _open = new(StringComparer.OrdinalIgnoreCase); private Form? _mainWindow; /// True, sobald das Beenden bestätigt wurde (der Launcher wertet das in FormClosing aus). public bool ShutdownConfirmed { get; private set; } public event Action? OpenStateChanged; public IReadOnlyList Views => _views; public void RegisterView(ModuleView view) => _views.Add(view); /// Setzt das Hauptfenster (Launcher) – Ziel für . public void SetMainWindow(Form main) => _mainWindow = main; public bool IsOpen(string viewId) => _open.TryGetValue(viewId, out var form) && !form.IsDisposed; public void ActivateMain() { if (_mainWindow is null || _mainWindow.IsDisposed) return; if (_mainWindow.WindowState == FormWindowState.Minimized) _mainWindow.WindowState = FormWindowState.Normal; _mainWindow.BringToFront(); _mainWindow.Activate(); } /// Sicherheitsabfrage; bei Bestätigung wird die Message-Loop beendet. public void RequestShutdown() { if (ShutdownConfirmed) return; var owner = _mainWindow is { IsDisposed: false } ? _mainWindow : null; var result = MessageBox.Show(owner, "IBKRTrader wirklich beenden? Laufende Worker/Services werden gestoppt.", "Beenden", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (result != DialogResult.OK) return; ShutdownConfirmed = true; Application.Exit(); } public void OpenView(string viewId) { var view = _views.FirstOrDefault(v => v.Id == viewId); if (view is not null) OpenView(view); } private void OpenView(ModuleView view) { if (_open.TryGetValue(view.Id, out var existing) && !existing.IsDisposed) { if (existing.WindowState == FormWindowState.Minimized) existing.WindowState = FormWindowState.Normal; existing.BringToFront(); existing.Activate(); return; } var form = view.CreateForm(); if (string.IsNullOrEmpty(form.Text) || form.Text == form.Name) form.Text = view.Title; // Gemeinsames „Fenster"-Menü in jedes Fenster injizieren (identische Shell-Chrome). if (form.MainMenuStrip is null) AttachWindowMenu(form, view.Id); _open[view.Id] = form; form.FormClosed += (_, _) => { _open.Remove(view.Id); OpenStateChanged?.Invoke(); }; form.Show(); OpenStateChanged?.Invoke(); } private void AttachWindowMenu(Form form, string currentViewId) { var menu = new MenuStrip { Dock = DockStyle.Top, ImageScalingSize = new Size(24, 24) }; WindowMenu.Wire(menu, this, currentViewId); form.Controls.Add(menu); form.MainMenuStrip = menu; } /// Schließt alle offenen View-Fenster (beim Herunterfahren). public void CloseAllViews() { foreach (var form in _open.Values.ToList()) if (!form.IsDisposed) form.Close(); _open.Clear(); } }