using IBKRTrader.Core.Logging; using IBKRTrader.Core.Modularity; using IBKRTrader.Core.Settings; using IBKRTrader.Core.Workers; using IBKRTrader.UI; using Microsoft.Extensions.DependencyInjection; namespace IBKRTrader; /// /// Launcher – das Basis-Fenster (Shell). Zeigt je registrierter View einen Button, führt beim Start /// Migrationen aus, startet Module und die WorkerEngine, und trägt das gemeinsame Fenster-Menü. /// Die inhaltlichen Ansichten (Logs, Settings, Workers, Module) sind eigenständige Fenster. /// public sealed class LauncherForm : Form { private readonly ShellUiHost _uiHost; private readonly IServiceProvider _services; private readonly LoggingService _logger; private readonly WorkerEngine _workerEngine; private readonly IReadOnlyList _modules; private readonly Dictionary _viewButtons = new(StringComparer.OrdinalIgnoreCase); private readonly ToolStripStatusLabel _status = new("Start..."); public LauncherForm(ShellUiHost uiHost, IServiceProvider services) { _uiHost = uiHost; _services = services; _logger = services.GetRequiredService(); _workerEngine = services.GetRequiredService(); _modules = services.GetServices().ToList(); Text = "IBKRTrader — Launcher"; Width = 720; Height = 540; StartPosition = FormStartPosition.CenterScreen; _uiHost.SetMainWindow(this); BuildUi(); _uiHost.OpenStateChanged += UpdateButtonStates; } private void BuildUi() { var menu = new MenuStrip { Dock = DockStyle.Top, ImageScalingSize = new Size(24, 24) }; WindowMenu.Wire(menu, _uiHost, null); // Fenster-Buttons in einem ToolStrip (Icon über Text) – wie im PolytraderSharp-Launcher. var toolstrip = new ToolStrip { Dock = DockStyle.Top, GripStyle = ToolStripGripStyle.Hidden, ImageScalingSize = new Size(32, 32), AutoSize = true, Padding = new Padding(4) }; foreach (var view in _uiHost.Views.OrderBy(v => v.Order).ThenBy(v => v.Title)) { var id = view.Id; var btn = new ToolStripButton(view.Title, view.Icon) { DisplayStyle = ToolStripItemDisplayStyle.ImageAndText, ImageScaling = ToolStripItemImageScaling.None, TextImageRelation = TextImageRelation.ImageAboveText, AutoSize = true, Padding = new Padding(6, 2, 6, 2) }; btn.Click += (_, _) => _uiHost.OpenView(id); _viewButtons[id] = btn; toolstrip.Items.Add(btn); } var content = new Panel { Dock = DockStyle.Fill, BackColor = SystemColors.ControlLightLight }; content.Controls.Add(new Label { Text = "IBKRTrader — Launcher\nFenster über die Leiste oben öffnen.", Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleCenter, ForeColor = SystemColors.GrayText, Font = new Font(Font.FontFamily, 11f) }); var statusStrip = new StatusStrip(); statusStrip.Items.Add(_status); // Dock-Stacking: zuletzt hinzugefügtes Top-Control liegt oben → Menü über ToolStrip. Controls.Add(content); Controls.Add(statusStrip); Controls.Add(toolstrip); Controls.Add(menu); MainMenuStrip = menu; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); _ = StartupAsync(); } protected override void OnFormClosing(FormClosingEventArgs e) { // Auch das Schließen-X läuft über die Sicherheitsabfrage. if (!_uiHost.ShutdownConfirmed) { e.Cancel = true; BeginInvoke((Action)(() => _uiHost.RequestShutdown())); return; } _uiHost.CloseAllViews(); try { foreach (var module in _modules) module.StopAsync(default).GetAwaiter().GetResult(); } catch { /* Shutdown darf nicht am Modul scheitern */ } // Worker/Services stoppt der Host in Program.Main via AppHost.StopAsync() nach Application.Run. base.OnFormClosing(e); } private async Task StartupAsync() { _logger.Info("Core", "=== IBKRTrader startet ==="); _logger.Info("Core", $"Version: 1.0.0 | .NET {Environment.Version}"); // Log-Level aus Settings. var levelStr = _services.GetRequiredService().Settings.Logging.Level; if (Enum.TryParse(levelStr, true, out var level)) _logger.SetMinLevel(level); // Das gesamte Schema (core_ + ct_) läuft über EF-Migrationen, extern via // `dotnet ef database update` angewendet – keine Laufzeit-Migration mehr. // Worker/Services laufen bereits (Generic Host, AppHost.Start()). Hier nur noch Modul-Start. foreach (var module in _modules) { try { await module.StartAsync(default); } catch (Exception ex) { _logger.Error(module.Name, $"{module.Name}: Start fehlgeschlagen.", ex); } } _logger.Info("Core", "IBKRTrader bereit."); SetStatus("Bereit"); UpdateButtonStates(); } private void UpdateButtonStates() { if (IsDisposed) return; if (InvokeRequired) { BeginInvoke((Action)UpdateButtonStates); return; } foreach (var (id, btn) in _viewButtons) btn.Checked = _uiHost.IsOpen(id); } private void SetStatus(string text) { if (IsDisposed) return; if (InvokeRequired) { BeginInvoke((Action)(() => SetStatus(text))); return; } _status.Text = $"Status: {text} | {DateTime.Now:HH:mm:ss}"; } }