Phase 5-UI: Designer-first — Launcher + TerminalView

- LauncherForm ist jetzt ein echtes Designer-Formular (LauncherForm.Designer.cs):
  ListView + MenuStrip + Button im Designer platziert/bearbeitbar. Zur Laufzeit
  werden nur noch ListView-Eintraege/Gruppen (DATEN) aus den registrierten Views
  befuellt + Events verdrahtet — keine Steuerelemente mehr im Code erzeugt.
- TerminalView: Combo-Items, Button-Text/-Farbe in den Designer verlagert;
  Konstruktor verdrahtet nur noch Verhalten.
- Inhaerente Ausnahme (Modul-System): die konkreten View-Eintraege sind Daten,
  keine vorplatzierbaren Controls -> daher datengetriebene ListView.
- Build 0 Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-02 11:23:23 +02:00
co-authored by Claude Opus 4.8
parent b0b65faf74
commit 1781d71983
6 changed files with 408 additions and 58 deletions
+26 -51
View File
@@ -1,17 +1,18 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Extensions.DependencyInjection;
using PolyTrader.Core.Modularity;
namespace PolyTraderSharp.Ui
{
/// <summary>
/// Schlanke „Startleiste" der PolyTrader.App. Rendert Buttons aus den registrierten
/// Views (gruppiert) und öffnet je View ein eigenständiges Fenster. Kennt selbst
/// kein Modul. Enthält übergangsweise einen Legacy-Button für das alte Tab-frm_main.
/// Schlanke „Startleiste" der PolyTrader.App. Das Layout (ListView, Menü, Button) liegt
/// im Designer (LauncherForm.Designer.cs). Zur Laufzeit werden ausschließlich die
/// Listeneinträge (Daten) aus den registrierten Views befüllt — keine Steuerelemente.
/// Kennt selbst kein Modul.
/// </summary>
public class LauncherForm : Form
public partial class LauncherForm : Form
{
private readonly ShellUiHost _uiHost;
private readonly IServiceProvider _services;
@@ -22,68 +23,42 @@ namespace PolyTraderSharp.Ui
_uiHost = uiHost;
_services = services;
Text = "PolyTrader";
Width = 360;
Height = 520;
StartPosition = FormStartPosition.CenterScreen;
MinimizeBox = true;
MaximizeBox = false;
FormBorderStyle = FormBorderStyle.FixedSingle;
InitializeComponent();
BuildUi();
PopulateViews();
lstViews.DoubleClick += (_, _) => OpenSelected();
btnOpen.Click += (_, _) => OpenSelected();
miLegacy.Click += (_, _) => OpenLegacy();
miBeenden.Click += (_, _) => Close();
}
private void BuildUi()
private void PopulateViews()
{
var flow = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.TopDown,
WrapContents = false,
AutoScroll = true,
Padding = new Padding(12)
};
lstViews.BeginUpdate();
lstViews.Groups.Clear();
lstViews.Items.Clear();
foreach (var group in _uiHost.Views
.GroupBy(v => string.IsNullOrEmpty(v.Group) ? "Allgemein" : v.Group)
.GroupBy(v => string.IsNullOrEmpty(v.Group) ? "Allgemein" : v.Group!)
.OrderBy(g => g.Key))
{
flow.Controls.Add(MakeHeader(group.Key));
var lvGroup = new ListViewGroup(group.Key) { Header = group.Key };
lstViews.Groups.Add(lvGroup);
foreach (var view in group.OrderBy(v => v.Order).ThenBy(v => v.Title))
{
var captured = view;
flow.Controls.Add(MakeButton(view.Title, (_, _) => _uiHost.OpenView(captured)));
lstViews.Items.Add(new ListViewItem(view.Title, lvGroup) { Tag = view });
}
}
// Übergangs-Zugang zur alten Tab-UI, bis alle Views extrahiert sind.
flow.Controls.Add(MakeHeader("Übergang"));
flow.Controls.Add(MakeButton("Legacy-UI (alte Tabs)", (_, _) => OpenLegacy()));
Controls.Add(flow);
lstViews.EndUpdate();
}
private Label MakeHeader(string text) => new()
private void OpenSelected()
{
Text = text,
AutoSize = true,
Font = new Font(Font, FontStyle.Bold),
Margin = new Padding(0, 10, 0, 4)
};
private Button MakeButton(string text, EventHandler onClick)
{
var btn = new Button
{
Text = text,
Width = 310,
Height = 34,
Margin = new Padding(0, 2, 0, 2),
TextAlign = ContentAlignment.MiddleLeft,
UseVisualStyleBackColor = true
};
btn.Click += onClick;
return btn;
if (lstViews.SelectedItems.Count == 0) return;
if (lstViews.SelectedItems[0].Tag is ModuleView view)
_uiHost.OpenView(view);
}
private void OpenLegacy()