using namespace System.Windows.Forms using namespace System.Drawing #region Class Definition class BaseComponent { static [SettingsManager] $SettingsManager static [LogManager] $LogManager static [AccessibilityManager] $AccessibilityManager static [void] InitializeShared([SettingsManager]$SettingsManager, [LogManager]$LogManager) { [BaseComponent]::SettingsManager = $SettingsManager [BaseComponent]::LogManager = $LogManager [BaseComponent]::AccessibilityManager = [AccessibilityManager]::new($SettingsManager, $LogManager) } [object] GetSetting([string]$key) { return [BaseComponent]::SettingsManager.Get($key) } [object] GetSetting([string]$key, [object]$defaultValue) { return [BaseComponent]::SettingsManager.Get($key, $defaultValue) } [void] SetSetting([string]$key, [object]$value) { [BaseComponent]::SettingsManager.Set($key, $value) } [void] LogInfo([string]$message) { if ($null -ne [BaseComponent]::LogManager) { [BaseComponent]::LogManager.Info($message) } } } class SettingsManager { [System.String]$SettingsPath [System.Collections.Hashtable]$Settings SettingsManager([System.String]$appName) { $appDataPath = [Environment]::GetFolderPath('ApplicationData') $appFolder = Join-Path $appDataPath $appName if (-not (Test-Path $appFolder)) { New-Item -Path $appFolder -ItemType Directory -Force | Out-Null } $this.SettingsPath = Join-Path $appFolder "settings.json" $this.Load() $this.EnsureDefaults() } [void] Load() { if (Test-Path $this.SettingsPath) { try { $json = Get-Content $this.SettingsPath -Raw | ConvertFrom-Json $this.Settings = @{} foreach ($prop in $json.PSObject.Properties) { $this.Settings[$prop.Name] = $prop.Value } } catch { Write-Warning "Fehler beim Laden der Settings: $_" $this.Settings = @{} } } else { $this.Settings = @{} } } # NEU: Stellt sicher, dass Standard-Einstellungen vorhanden sind [void] EnsureDefaults() { if (-not $this.Settings.ContainsKey("MenuStructure")) { $this.Settings["MenuStructure"] = $this.GetDefaultMenuStructure() $this.Save() } if (-not $this.Settings.ContainsKey("WindowWidth")) { $this.Settings["WindowWidth"] = 1024 } if (-not $this.Settings.ContainsKey("WindowHeight")) { $this.Settings["WindowHeight"] = 768 } if (-not $this.Settings.ContainsKey("SplitterDistance")) { $this.Settings["SplitterDistance"] = 200 } if (-not $this.Settings.ContainsKey("TemplatePath")) { $this.Settings["TemplatePath"] = (Join-Path -Path $this.Settings["RootPath"] -ChildPath "Templates") } if (-not $this.Settings.ContainsKey("FunctionsPath")) { $this.Settings["FunctionsPath"] = (Join-Path -Path $this.Settings["RootPath"] -ChildPath "Functions") } if (-not $this.Settings.ContainsKey("DeploymentPath")) { $this.Settings["DeploymentPath"] = (Join-Path -Path $this.Settings["RootPath"] -ChildPath "Deployments") } } # NEU: Standard-Menüstruktur [object] GetDefaultMenuStructure() { return @( @{ Name = "Datei" Text = "Datei" Items = @( @{ Name = "Import"; Text = "Import" } @{ Name = "Export"; Text = "Export" } @{ Name = "Refresh"; Text = "Aktualisieren" } @{ Type = "Separator" } @{ Name = "Exit"; Text = "Beenden" } ) }, @{ Name = "Settings" Text = "Einstellungen" Items = @( @{ Name = "OpenFolder"; Text = "Ordner wählen..." } @{ Name = "ResetMenu"; Text = "Menü zurücksetzen" } ) }, @{ Name = "TemplateDesigner" Text = "Template Designer" }, @{ Name = "Help" Text = "Hilfe" Items = @( @{ Name = "About"; Text = "Über..." } ) } ) } [void] Save() { try { $this.Settings | ConvertTo-Json -Depth 10 | Set-Content $this.SettingsPath -Encoding UTF8 } catch { Write-Warning "Fehler beim Speichern der Settings: $_" } } [object] Get([System.String]$key) { return $this.Settings[$key] } [object] Get([System.String]$key, [object]$defaultValue) { if ($this.Settings.ContainsKey($key)) { return $this.Settings[$key] } return $defaultValue } [void] Set([System.String]$key, [object]$value) { $this.Settings[$key] = $value $this.Save() } [bool] Contains([System.String]$key) { return $this.Settings.ContainsKey($key) } [void] Remove([System.String]$key) { if ($this.Settings.ContainsKey($key)) { $this.Settings.Remove($key) $this.Save() } } [System.Collections.Hashtable] GetAll() { return $this.Settings } # NEU: Holt die Menüstruktur [System.Array] GetMenuStructure() { if ($this.Settings.ContainsKey("MenuStructure")) { # Konvertiere PSCustomObject zurück zu Hashtable return $this.ConvertToHashtable($this.Settings["MenuStructure"]) } return $this.GetDefaultMenuStructure() } # NEU: Setzt eine neue Menüstruktur [void] SetMenuStructure([System.Array]$menuStructure) { $this.Settings["MenuStructure"] = $menuStructure $this.Save() } # NEU: Setzt Menü auf Standard zurück [void] ResetMenuStructure() { $this.Settings["MenuStructure"] = $this.GetDefaultMenuStructure() $this.Save() } # Hilfsfunktion: Konvertiert PSCustomObject zu Hashtable (für JSON-Deserialisierung) hidden [object] ConvertToHashtable([object]$obj) { if ($obj -is [System.Management.Automation.PSCustomObject]) { $hash = @{} foreach ($property in $obj.PSObject.Properties) { $hash[$property.Name] = $this.ConvertToHashtable($property.Value) } return $hash } elseif ($obj -is [System.Array] -or $obj -is [System.Collections.ArrayList]) { $array = @() foreach ($item in $obj) { $array += $this.ConvertToHashtable($item) } return $array } else { return $obj } } } class StateManager { [System.Collections.Hashtable] $State = @{} [System.Collections.ArrayList] $Buttons = @() [System.Collections.Hashtable] $MenuItems = @{} [System.Collections.ArrayList] $Forms = @() # Registriert einen Button mit seiner Bedingung [void] RegisterButton([ButtonBuilder]$buttonBuilder) { $this.Buttons.Add($buttonBuilder) | Out-Null } # NEU: Registriert ein Menü-Item [void] RegisterMenuItem([string]$menuName, [MenuBuilder]$menuBuilder) { $this.MenuItems[$menuName] = $menuBuilder } # Registriert einen Button mit seiner Bedingung [void] RegisterForm([System.Object]$form) { $this.Forms.Add($form) | Out-Null } # Setzt einen State-Wert und aktualisiert alle Buttons und Menüs [void] SetState([string]$key, [object]$value) { $this.State[$key] = $value $this.UpdateAll() } [void] SetStates([hashtable]$states) { foreach ($key in $states.Keys) { $this.State[$key] = $states[$key] } $this.UpdateAll() } # Holt einen State-Wert [object] GetState([string]$key) { return $this.State[$key] } [hashtable] GetAllStates() { return $this.State.Clone() } # Aktualisiert alle registrierten Buttons [void] UpdateAllButtons() { foreach ($button in $this.Buttons) { $button.UpdateEnabledState() } } # NEU: Aktualisiert alle registrierten Menü-Items [void] UpdateAllMenuItems() { foreach ($menuName in $this.MenuItems.Keys) { $this.MenuItems[$menuName].UpdateMenuItemState($menuName) } } # Aktualisiert alle registrierten Buttons [void] UpdateAllForms() { foreach ($form in $this.Forms) { $form.UpdateVisibleState() } } # NEU: Aktualisiert alles (Buttons + Menüs) [void] UpdateAll() { $this.UpdateAllButtons() $this.UpdateAllMenuItems() $this.UpdateAllForms() } } class LogManager { [System.String]$LogPath [bool]$EnableConsole = $false LogManager([System.String]$appName) { $appDataPath = [Environment]::GetFolderPath('ApplicationData') $logFolder = Join-Path $appDataPath "$appName\Logs" if (-not (Test-Path $logFolder)) { New-Item -Path $logFolder -ItemType Directory -Force | Out-Null } $timestamp = Get-Date -Format "yyyyMMdd" $this.LogPath = Join-Path $logFolder "log_$timestamp.txt" } [void] Info([string]$message) { $this.Write("INFO", $message) } [void] Warning([string]$message) { $this.Write("WARN", $message) } [void] Error([string]$message) { $this.Write("ERROR", $message) } hidden [void] Write([string]$level, [string]$message) { $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logLine = "[$timestamp] [$level] $message" Add-Content -Path $this.LogPath -Value $logLine if ($this.EnableConsole) { Write-Host $logLine } } } class AccessibilityManager { [SettingsManager] $SettingsManager [LogManager] $LogManager [System.Collections.ArrayList] $FontControls = @() [System.Collections.ArrayList] $Forms = @() [System.Collections.ArrayList] $LayoutContainers = @() [hashtable] $AccessibilitySettings = @{} AccessibilityManager([SettingsManager]$SettingsManager, [LogManager]$LogManager) { $this.SettingsManager = $SettingsManager $this.LogManager = $LogManager $this.InitializeDefaults() } # Initialisiert Standard-Accessibility-Einstellungen [void] InitializeDefaults() { $this.AccessibilitySettings = @{ FontSize = @{ Min = 6; Max = 32; Default = 9; Step = 1 } FontSizeStep = @{ Min = 1; Max = 5; Default = 1 } HighContrast = @{ Default = $false } KeyboardShortcuts = @{ Default = $true } ScreenReaderSupport = @{ Default = $false } } # Stelle sicher, dass alle Settings existieren foreach ($key in $this.AccessibilitySettings.Keys) { if (-not $this.SettingsManager.Contains($key)) { $this.SettingsManager.Set($key, $this.AccessibilitySettings[$key].Default) } } } # Registriert ein Form für Accessibility-Features [void] RegisterForm([System.Windows.Forms.Form]$form) { if (-not $this.Forms.Contains($form)) { $this.Forms.Add($form) | Out-Null $this.AttachMouseWheelHandler($form) $this.AttachKeyboardShortcuts($form) $this.LogManager.Info("Form registriert für Accessibility-Features") } } # Registriert Container für automatisches Layout-Update [void] RegisterLayoutContainer([System.Windows.Forms.Control]$container) { if (-not $this.LayoutContainers.Contains($container)) { $this.LayoutContainers.Add($container) | Out-Null $this.LogManager.Info("Layout-Container registriert: $($container.GetType().Name)") } } # Registriert ein Control für Font-Updates [void] RegisterFontControl([System.Windows.Forms.Control]$control) { if (-not $this.FontControls.Contains($control)) { $this.FontControls.Add($control) | Out-Null $this.ApplyFontSize($control) } } # Registriert mehrere Controls [void] RegisterFontControls([System.Windows.Forms.Control[]]$controls) { foreach ($control in $controls) { $this.RegisterFontControl($control) } } # Registriert alle Controls eines Forms/Containers rekursiv [void] RegisterAllControls([System.Windows.Forms.Control]$container) { # Registriere das Container-Control selbst (wenn es Text hat) if ($container.GetType().GetProperty("Font") -ne $null) { $this.RegisterFontControl($container) } # Durchlaufe alle Child-Controls rekursiv foreach ($control in $container.Controls) { $this.RegisterAllControls($control) } $this.LogManager.Info("Alle Controls von '$($container.GetType().Name)' registriert (Total: $($this.FontControls.Count))") } # Optional: Registriert nur bestimmte Control-Typen [void] RegisterControlsByType([System.Windows.Forms.Control]$container, [Type[]]$types) { foreach ($control in $container.Controls) { if ($types -contains $control.GetType()) { $this.RegisterFontControl($control) } # Rekursiv durch Child-Controls if ($control.Controls.Count -gt 0) { $this.RegisterControlsByType($control, $types) } } } # Fügt MouseWheel-Handler für Strg + Mausrad hinzu hidden [void] AttachMouseWheelHandler([System.Windows.Forms.Form]$form) { $form.Add_MouseWheel({ param($sender, $e) if ([System.Windows.Forms.Control]::ModifierKeys -eq [System.Windows.Forms.Keys]::Control) { $delta = if ($e.Delta -gt 0) { 1 } else { -1 } [BaseComponent]::AccessibilityManager.ChangeFontSize($delta) $e.Handled = $true } }.GetNewClosure()) } # Fügt Tastatur-Shortcuts hinzu hidden [void] AttachKeyboardShortcuts([System.Windows.Forms.Form]$form) { $form.KeyPreview = $true $form.Add_KeyDown({ param($sender, $e) # Strg + 0: Reset auf Standard if ($e.Control -and $e.KeyCode -eq [System.Windows.Forms.Keys]::D0) { [BaseComponent]::AccessibilityManager.ResetFontSize() $e.Handled = $true } # Strg + Plus: Größer if ($e.Control -and ($e.KeyCode -eq [System.Windows.Forms.Keys]::Add -or $e.KeyCode -eq [System.Windows.Forms.Keys]::Oemplus)) { [BaseComponent]::AccessibilityManager.ChangeFontSize(1) $e.Handled = $true } # Strg + Minus: Kleiner if ($e.Control -and ($e.KeyCode -eq [System.Windows.Forms.Keys]::Subtract -or $e.KeyCode -eq [System.Windows.Forms.Keys]::OemMinus)) { [BaseComponent]::AccessibilityManager.ChangeFontSize(-1) $e.Handled = $true } # F11: High Contrast Toggle (optional) if ($e.KeyCode -eq [System.Windows.Forms.Keys]::F11) { [BaseComponent]::AccessibilityManager.ToggleHighContrast() $e.Handled = $true } }.GetNewClosure()) } # Ändert die Schriftgröße [void] ChangeFontSize([int]$delta) { $settings = $this.AccessibilitySettings["FontSize"] $currentSize = $this.SettingsManager.Get("FontSize", $settings.Default) $step = $this.SettingsManager.Get("FontSizeStep", 1) $newSize = $currentSize + ($delta * $step) # Begrenzung if ($newSize -lt $settings.Min) { $newSize = $settings.Min } if ($newSize -gt $settings.Max) { $newSize = $settings.Max } if ($newSize -ne $currentSize) { $this.SettingsManager.Set("FontSize", $newSize) $this.UpdateAllFonts() $this.LogManager.Info("Schriftgröße geändert: $currentSize -> $newSize") } } # Setzt Schriftgröße zurück [void] ResetFontSize() { $defaultSize = $this.AccessibilitySettings["FontSize"].Default $this.SettingsManager.Set("FontSize", $defaultSize) $this.UpdateAllFonts() $this.LogManager.Info("Schriftgröße zurückgesetzt auf: $defaultSize") } # Wendet Schriftgröße auf ein Control an hidden [void] ApplyFontSize([System.Windows.Forms.Control]$control) { $fontSize = $this.SettingsManager.Get("FontSize", 9) try { $control.Font = New-Object System.Drawing.Font($control.Font.FontFamily, $fontSize) } catch { $this.LogManager.Warning("Fehler beim Setzen der Schriftgröße für Control: $_") } } # Aktualisiert alle registrierten Controls [void] UpdateAllFonts() { foreach ($control in $this.FontControls) { if ($null -ne $control -and -not $control.IsDisposed) { $this.ApplyFontSize($control) } } $this.UpdateAllLayouts() } # Aktualisiert das Layout aller registrierten Container hidden [void] UpdateAllLayouts() { foreach ($container in $this.LayoutContainers) { if ($null -ne $container -and -not $container.IsDisposed) { # GroupBoxes: AutoSize aktivieren if ($container -is [System.Windows.Forms.GroupBox]) { $container.AutoSize = $true $container.AutoSizeMode = 'GrowAndShrink' } # FlowLayoutPanel: PerformLayout erzwingen if ($container -is [System.Windows.Forms.FlowLayoutPanel]) { foreach ($child in $container.Controls) { if ($child -is [System.Windows.Forms.GroupBox]) { $child.AutoSize = $true $child.AutoSizeMode = 'GrowAndShrink' # Breite anpassen $child.Width = $container.ClientSize.Width - 20 } } $container.PerformLayout() $container.Refresh() } # Panel: PerformLayout if ($container -is [System.Windows.Forms.Panel]) { $container.PerformLayout() } } } $this.LogManager.Info("Layout-Update abgeschlossen") } # Schaltet High Contrast Modus um [void] ToggleHighContrast() { $currentState = $this.SettingsManager.Get("HighContrast", $false) $newState = -not $currentState $this.SettingsManager.Set("HighContrast", $newState) $this.ApplyHighContrast($newState) $this.LogManager.Info("High Contrast: $newState") } # Wendet High Contrast auf alle Forms an hidden [void] ApplyHighContrast([bool]$enabled) { foreach ($form in $this.Forms) { if ($null -ne $form -and -not $form.IsDisposed) { if ($enabled) { # Form-Hintergrund $form.BackColor = [System.Drawing.Color]::Black $form.ForeColor = [System.Drawing.Color]::White # NEU: Rekursiv alle Controls anpassen $this.ApplyHighContrastToControls($form, $enabled) } else { $form.BackColor = [System.Drawing.SystemColors]::Control $form.ForeColor = [System.Drawing.SystemColors]::ControlText # NEU: Rekursiv alle Controls zurücksetzen $this.ApplyHighContrastToControls($form, $enabled) } } } } # NEU: Wendet High Contrast rekursiv auf alle Controls an hidden [void] ApplyHighContrastToControls([System.Windows.Forms.Control]$control, [bool]$enabled) { if ($enabled) { # Schwarzer Hintergrund → Weiße Schrift switch ($control.GetType().Name) { 'Panel' { $control.BackColor = [System.Drawing.Color]::Black $control.ForeColor = [System.Drawing.Color]::White } 'FlowLayoutPanel' { $control.BackColor = [System.Drawing.Color]::Black $control.ForeColor = [System.Drawing.Color]::White } 'GroupBox' { $control.BackColor = [System.Drawing.Color]::Black $control.ForeColor = [System.Drawing.Color]::White } 'Label' { $control.ForeColor = [System.Drawing.Color]::White # Transparent oder schwarz if ($control.BackColor -ne [System.Drawing.Color]::Transparent) { $control.BackColor = [System.Drawing.Color]::Black } } 'Button' { $control.BackColor = [System.Drawing.Color]::FromArgb(40, 40, 40) $control.ForeColor = [System.Drawing.Color]::White $control.FlatStyle = 'Flat' } 'TextBox' { $control.BackColor = [System.Drawing.Color]::FromArgb(30, 30, 30) $control.ForeColor = [System.Drawing.Color]::White } 'TreeView' { $control.BackColor = [System.Drawing.Color]::FromArgb(20, 20, 20) $control.ForeColor = [System.Drawing.Color]::White } 'ListBox' { $control.BackColor = [System.Drawing.Color]::FromArgb(20, 20, 20) $control.ForeColor = [System.Drawing.Color]::White } 'DataGridView' { $control.BackgroundColor = [System.Drawing.Color]::Black $control.ForeColor = [System.Drawing.Color]::White $control.DefaultCellStyle.BackColor = [System.Drawing.Color]::FromArgb(30, 30, 30) $control.DefaultCellStyle.ForeColor = [System.Drawing.Color]::White $control.ColumnHeadersDefaultCellStyle.BackColor = [System.Drawing.Color]::FromArgb(50, 50, 50) $control.ColumnHeadersDefaultCellStyle.ForeColor = [System.Drawing.Color]::White } 'TabControl' { $control.BackColor = [System.Drawing.Color]::Black $control.ForeColor = [System.Drawing.Color]::White } 'TabPage' { $control.BackColor = [System.Drawing.Color]::Black $control.ForeColor = [System.Drawing.Color]::White } } } else { # Zurück zu Standard-Farben switch ($control.GetType().Name) { 'Panel' { $control.BackColor = [System.Drawing.SystemColors]::Control $control.ForeColor = [System.Drawing.SystemColors]::ControlText } 'FlowLayoutPanel' { $control.BackColor = [System.Drawing.SystemColors]::Control $control.ForeColor = [System.Drawing.SystemColors]::ControlText } 'GroupBox' { $control.BackColor = [System.Drawing.SystemColors]::Control $control.ForeColor = [System.Drawing.SystemColors]::ControlText } 'Label' { $control.ForeColor = [System.Drawing.SystemColors]::ControlText if ($control.BackColor -ne [System.Drawing.Color]::Transparent) { $control.BackColor = [System.Drawing.SystemColors]::Control } } 'Button' { $control.BackColor = [System.Drawing.SystemColors]::Control $control.ForeColor = [System.Drawing.SystemColors]::ControlText $control.FlatStyle = 'Standard' } 'TextBox' { $control.BackColor = [System.Drawing.SystemColors]::Window $control.ForeColor = [System.Drawing.SystemColors]::WindowText } 'TreeView' { $control.BackColor = [System.Drawing.SystemColors]::Window $control.ForeColor = [System.Drawing.SystemColors]::WindowText } 'ListBox' { $control.BackColor = [System.Drawing.SystemColors]::Window $control.ForeColor = [System.Drawing.SystemColors]::WindowText } 'DataGridView' { $control.BackgroundColor = [System.Drawing.SystemColors]::Control $control.ForeColor = [System.Drawing.SystemColors]::ControlText $control.DefaultCellStyle.BackColor = [System.Drawing.SystemColors]::Window $control.DefaultCellStyle.ForeColor = [System.Drawing.SystemColors]::WindowText $control.ColumnHeadersDefaultCellStyle.BackColor = [System.Drawing.SystemColors]::Control $control.ColumnHeadersDefaultCellStyle.ForeColor = [System.Drawing.SystemColors]::ControlText } 'TabControl' { $control.BackColor = [System.Drawing.SystemColors]::Control $control.ForeColor = [System.Drawing.SystemColors]::ControlText } 'TabPage' { $control.BackColor = [System.Drawing.SystemColors]::Control $control.ForeColor = [System.Drawing.SystemColors]::ControlText } } } # Rekursiv für alle Child-Controls foreach ($child in $control.Controls) { $this.ApplyHighContrastToControls($child, $enabled) } } # Gibt aktuelle Accessibility-Einstellungen zurück [hashtable] GetCurrentSettings() { return @{ FontSize = $this.SettingsManager.Get("FontSize", 9) HighContrast = $this.SettingsManager.Get("HighContrast", $false) KeyboardShortcuts = $this.SettingsManager.Get("KeyboardShortcuts", $true) } } # Setzt eine spezifische Accessibility-Einstellung [void] SetAccessibilitySetting([string]$key, [object]$value) { $this.SettingsManager.Set($key, $value) # Wende Änderung sofort an switch ($key) { "FontSize" { $this.UpdateAllFonts() } "HighContrast" { $this.ApplyHighContrast($value) } } } } class FormBuilder : BaseComponent { [System.String] $Title [System.Windows.Forms.Form] $Form [MenuBuilder] $Menu [StatusStripBuilder] $StatusBar FormBuilder($Title){ $this.Form = New-Object System.Windows.Forms.Form $this.Form.Text = $Title $this.Form.StartPosition = "CenterScreen" # HIER wird RegisterForm automatisch aufgerufen: [BaseComponent]::AccessibilityManager.RegisterForm($this.Form) } # Registriert ein Control für Accessibility-Features [FormBuilder] RegisterAccessibleControl([System.Windows.Forms.Control]$control) { [BaseComponent]::AccessibilityManager.RegisterFontControl($control) return $this } # Registriert mehrere Controls [FormBuilder] RegisterAccessibleControls([System.Windows.Forms.Control[]]$controls) { [BaseComponent]::AccessibilityManager.RegisterFontControls($controls) return $this } # NEU: Registriert automatisch ALLE Controls des Forms [FormBuilder] RegisterAllAccessibleControls() { [BaseComponent]::AccessibilityManager.RegisterAllControls($this.Form) return $this } # NEU: Registriert nur bestimmte Control-Typen (z.B. nur TextBoxen und Labels) [FormBuilder] RegisterAccessibleControlsByType([Type[]]$types) { [BaseComponent]::AccessibilityManager.RegisterControlsByType($this.Form, $types) return $this } [FormBuilder] AddMenu([MenuBuilder]$menuBuilder) { $this.Menu = $menuBuilder $this.Form.Controls.Add($menuBuilder.GetMenuStrip()) return $this } # Fügt eine StatusBar hinzu [FormBuilder] AddStatusBar([StatusStripBuilder]$statusBar) { $this.StatusBar = $statusBar $this.Form.Controls.Add($statusBar.GetStatusStrip()) return $this } # Fügt ein SplitPanel hinzu [FormBuilder] AddSplitPanel([SplitPanelBuilder]$splitPanel) { $this.Form.Controls.Add($splitPanel.GetSplitContainer()) return $this } # Fügt ein beliebiges Control hinzu [FormBuilder] AddControl([System.Windows.Forms.Control]$control) { $this.Form.Controls.Add($control) return $this } # Fügt mehrere Controls hinzu [FormBuilder] AddControls([System.Windows.Forms.Control[]]$controls) { foreach ($control in $controls) { $this.Form.Controls.Add($control) } return $this } [void] SetTitle($Title) { $this.Form.Text = $Title } # Setzt die Größe des Forms [FormBuilder] SetSize([int]$width, [int]$height) { $this.Form.Size = New-Object System.Drawing.Size($width, $height) return $this } # Setzt nur die Breite [FormBuilder] SetWidth([int]$width) { $this.Form.Width = $width return $this } # Setzt nur die Höhe [FormBuilder] SetHeight([int]$height) { $this.Form.Height = $height return $this } # Setzt die Mindestgröße [FormBuilder] SetMinimumSize([int]$width, [int]$height) { $this.Form.MinimumSize = New-Object System.Drawing.Size($width, $height) return $this } # Setzt die Maximalgröße [FormBuilder] SetMaximumSize([int]$width, [int]$height) { $this.Form.MaximumSize = New-Object System.Drawing.Size($width, $height) return $this } # Setzt die Startposition [FormBuilder] SetStartPosition([System.String]$position) { $this.Form.StartPosition = [System.Windows.Forms.FormStartPosition]::$position return $this } # Setzt die Location (Position auf dem Bildschirm) [FormBuilder] SetLocation([int]$x, [int]$y) { $this.Form.Location = New-Object System.Drawing.Point($x, $y) return $this } # Setzt den WindowState [FormBuilder] SetWindowState([System.String]$state) { $this.Form.WindowState = [System.Windows.Forms.FormWindowState]::$state return $this } [FormBuilder] AddLoadHandler([scriptblock]$handler) { $this.Form.Add_Load($handler) return $this } # Shown Event (wenn Form zum ersten Mal angezeigt wird) [FormBuilder] AddShownHandler([scriptblock]$handler) { $this.Form.Add_Shown($handler) return $this } # FormClosing Event (bevor Form geschlossen wird - kann abgebrochen werden) [FormBuilder] AddFormClosingHandler([scriptblock]$handler) { $this.Form.Add_FormClosing($handler) return $this } # FormClosed Event (nachdem Form geschlossen wurde) [FormBuilder] AddFormClosedHandler([scriptblock]$handler) { $this.Form.Add_FormClosed($handler) return $this } # Resize Event [FormBuilder] AddResizeHandler([scriptblock]$handler) { $this.Form.Add_Resize($handler) return $this } # Move Event [FormBuilder] AddMoveHandler([scriptblock]$handler) { $this.Form.Add_Move($handler) return $this } # KeyDown Event [FormBuilder] AddKeyDownHandler([scriptblock]$handler) { $this.Form.Add_KeyDown($handler) return $this } # Activate Event (wenn Form aktiviert wird) [FormBuilder] AddActivateHandler([scriptblock]$handler) { $this.Form.Add_Activated($handler) return $this } # Deactivate Event [FormBuilder] AddDeactivateHandler([scriptblock]$handler) { $this.Form.Add_Deactivate($handler) return $this } # Generischer Event-Handler für alle Events [FormBuilder] AddEventHandler([System.String]$eventName, [scriptblock]$handler) { $this.Form."Add_$eventName"($handler) return $this } [void] Show(){ [void]$this.Form.ShowDialog() } } class MenuBuilder : BaseComponent{ [System.Windows.Forms.MenuStrip] $MenuStrip [System.Collections.Hashtable] $MenuItems = @{} [System.Collections.Hashtable] $MenuConditions = @{} [StateManager] $StateManager = $null MenuBuilder() { $this.MenuStrip = New-Object System.Windows.Forms.MenuStrip $this.MenuStrip.Dock = 'Top' } # NEU: Setzt den StateManager [MenuBuilder] WithStateManager([StateManager]$stateManager) { $this.StateManager = $stateManager return $this } # Fügt ein Hauptmenü-Element hinzu - JETZT MIT RETURN [MenuBuilder] AddMainMenu([System.String]$name, [System.String]$text) { $menuItem = New-Object System.Windows.Forms.ToolStripMenuItem $text $this.MenuStrip.Items.Add($menuItem) | Out-Null $this.MenuItems[$name] = $menuItem return $this | Out-Null } # Fügt ein Untermenü-Element hinzu - JETZT MIT RETURN [MenuBuilder] AddSubMenu([System.String]$parentName, [System.String]$name, [System.String]$text) { if ($this.MenuItems.ContainsKey($parentName)) { $menuItem = New-Object System.Windows.Forms.ToolStripMenuItem $text $this.MenuItems[$parentName].DropDownItems.Add($menuItem) | Out-Null $this.MenuItems[$name] = $menuItem return $this | Out-Null } throw "Parent menu '$parentName' nicht gefunden" } # NEU: Fügt mehrere Untermenüs auf einmal hinzu [MenuBuilder] AddSubMenus([System.String]$parentName, [hashtable[]]$items) { foreach ($item in $items) { if ($item.Type -eq "Separator") { $this.AddSeparator($parentName) } else { $this.AddSubMenu($parentName, $item.Name, $item.Text) if ($item.Handler) { $this.SetClickHandler($item.Name, $item.Handler) } } } return $this } # NEU: Erstellt komplettes Menü aus einer Struktur [MenuBuilder] BuildFromStructure([hashtable[]]$structure) { foreach ($mainMenu in $structure) { $this.AddMainMenu($mainMenu.Name, $mainMenu.Text) if ($mainMenu.Items) { $this.AddSubMenus($mainMenu.Name, $mainMenu.Items) } } return $this } # Fügt einen Separator hinzu - JETZT MIT RETURN [MenuBuilder] AddSeparator([System.String]$parentName) { if ($this.MenuItems.ContainsKey($parentName)) { $this.MenuItems[$parentName].DropDownItems.Add('-') | Out-Null } return $this | Out-Null } # Setzt einen Event-Handler für ein Menü-Element - JETZT MIT RETURN [MenuBuilder] SetClickHandler([System.String]$menuName, [scriptblock]$handler) { if ($this.MenuItems.ContainsKey($menuName)) { $this.MenuItems[$menuName].Add_Click($handler) } return $this | Out-Null } # NEU: Setzt einen Click-Handler mit Enable-Bedingung [MenuBuilder] SetClickHandler([System.String]$menuName, [scriptblock]$handler, [scriptblock]$enableCondition) { if ($this.MenuItems.ContainsKey($menuName)) { $this.MenuItems[$menuName].Add_Click($handler) $this.MenuConditions[$menuName] = $enableCondition # Initial den Status setzen $this.UpdateMenuItemState($menuName) # Bei StateManager registrieren if ($null -ne $this.StateManager) { $this.StateManager.RegisterMenuItem($menuName, $this) } } return $this | Out-Null } # NEU: Setzt mehrere Click-Handler auf einmal [MenuBuilder] SetClickHandlers([System.Collections.Hashtable]$handlers) { foreach ($key in $handlers.Keys) { $this.SetClickHandler($key, $handlers[$key]) } return $this | Out-Null } # NEU: Setzt mehrere Click-Handler mit Bedingungen [MenuBuilder] SetClickHandlersWithConditions([System.Collections.Hashtable]$handlers) { foreach ($key in $handlers.Keys) { $config = $handlers[$key] if ($config.Condition) { $this.SetClickHandler($key, $config.Handler, $config.Condition) } else { $this.SetClickHandler($key, $config.Handler) } } return $this | Out-Null } # NEU: Aktualisiert den Enabled-Status eines Menü-Items [void] UpdateMenuItemState([System.String]$menuName) { if ($this.MenuItems.ContainsKey($menuName) -and $this.MenuConditions.ContainsKey($menuName)) { try { $result = & $this.MenuConditions[$menuName] $this.MenuItems[$menuName].Enabled = [bool]$result } catch { Write-Warning "Fehler beim Evaluieren der Menu-Bedingung für '$menuName': $_" $this.MenuItems[$menuName].Enabled = $false } } } # NEU: Aktualisiert alle Menü-Items mit Bedingungen [void] UpdateAllMenuItems() { foreach ($menuName in $this.MenuConditions.Keys) { $this.UpdateMenuItemState($menuName) } } # Gibt das MenuStrip-Objekt zurück [System.Windows.Forms.MenuStrip] GetMenuStrip() { return $this.MenuStrip } # Aktiviert/Deaktiviert ein Menü-Element - JETZT MIT RETURN [MenuBuilder] SetEnabled([System.String]$menuName, [bool]$enabled) { if ($this.MenuItems.ContainsKey($menuName)) { $this.MenuItems[$menuName].Enabled = $enabled } return $this } # NEU: Holt ein Menü-Item [System.Windows.Forms.ToolStripMenuItem] GetMenuItem([System.String]$menuName) { if ($this.MenuItems.ContainsKey($menuName)) { return $this.MenuItems[$menuName] } return $null } } class StatusStripBuilder : BaseComponent{ [System.Windows.Forms.StatusStrip] $StatusStrip [System.Collections.Hashtable] $Labels = @{} StatusStripBuilder() { $this.StatusStrip = New-Object System.Windows.Forms.StatusStrip } # Fügt ein Label zur StatusBar hinzu [System.Windows.Forms.ToolStripStatusLabel] AddLabel([System.String]$name, [System.String]$text) { $label = New-Object System.Windows.Forms.ToolStripStatusLabel $label.Text = $text $this.StatusStrip.Items.Add($label) | Out-Null $this.Labels[$name] = $label return $label } # Fügt ein Label mit Spring-Eigenschaft hinzu (füllt verfügbaren Platz) [System.Windows.Forms.ToolStripStatusLabel] AddSpringLabel([System.String]$name, [System.String]$text) { $label = New-Object System.Windows.Forms.ToolStripStatusLabel $label.Text = $text $label.Spring = $true $label.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft $this.StatusStrip.Items.Add($label) | Out-Null $this.Labels[$name] = $label return $label } # Setzt den Text eines Labels [void] SetText([System.String]$labelName, [System.String]$text) { if ($this.Labels.ContainsKey($labelName)) { $this.Labels[$labelName].Text = $text } } # Fügt einen Separator hinzu [void] AddSeparator() { $separator = New-Object System.Windows.Forms.ToolStripSeparator $this.StatusStrip.Items.Add($separator) | Out-Null } # Fügt eine ProgressBar hinzu [System.Windows.Forms.ToolStripProgressBar] AddProgressBar([System.String]$name) { $progressBar = New-Object System.Windows.Forms.ToolStripProgressBar $progressBar.Size = New-Object System.Drawing.Size(100, 16) $this.StatusStrip.Items.Add($progressBar) | Out-Null $this.Labels[$name] = $progressBar return $progressBar } # Setzt den Fortschritt einer ProgressBar [void] SetProgress([System.String]$progressBarName, [int]$value) { if ($this.Labels.ContainsKey($progressBarName)) { $this.Labels[$progressBarName].Value = $value } } # Gibt das StatusStrip-Objekt zurück [System.Windows.Forms.StatusStrip] GetStatusStrip() { return $this.StatusStrip } # Zeigt/Versteckt ein Element [void] SetVisible([System.String]$itemName, [bool]$visible) { if ($this.Labels.ContainsKey($itemName)) { $this.Labels[$itemName].Visible = $visible } } # Setzt die Hintergrundfarbe eines Labels [void] SetBackColor([System.String]$labelName, [System.Drawing.Color]$color) { if ($this.Labels.ContainsKey($labelName)) { $this.Labels[$labelName].BackColor = $color } } # Setzt die Textfarbe eines Labels [void] SetForeColor([System.String]$labelName, [System.Drawing.Color]$color) { if ($this.Labels.ContainsKey($labelName)) { $this.Labels[$labelName].ForeColor = $color } } } class SplitPanelBuilder : BaseComponent{ [System.Windows.Forms.SplitContainer] $SplitContainer [Int] $DesiredPanel1MinSize = 100 [Int] $DesiredPanel2MinSize = 250 [Int] $DesiredSplitterDistance = 200 [Double] $SplitterDistanceRatio = 0.0 SplitPanelBuilder([System.String]$orientation) { $this.SplitContainer = New-Object System.Windows.Forms.SplitContainer $this.SplitContainer.Dock = 'Fill' if ($orientation -eq 'Horizontal') { $this.SplitContainer.Orientation = 'Horizontal' } else { $this.SplitContainer.Orientation = 'Vertical' } } # Konstruktor mit MinSize-Optionen - Verzögerte Initialisierung SplitPanelBuilder([System.String]$orientation, [int]$splitterDistance, [int]$panel1MinSize, [int]$panel2MinSize) { $this.SplitContainer = New-Object System.Windows.Forms.SplitContainer $this.SplitContainer.Dock = 'Fill' $this.SplitContainer.Orientation = $orientation # Speichere die gewünschten Werte für spätere Initialisierung $this.DesiredPanel1MinSize = $panel1MinSize $this.DesiredPanel2MinSize = $panel2MinSize $this.DesiredSplitterDistance = $splitterDistance # Setze nur die absolut notwendigen Anfangswerte $this.SplitContainer.Panel1MinSize = [Math]::Min($panel1MinSize, 25) $this.SplitContainer.Panel2MinSize = 25 } # Konstruktor mit Verhältnis (Ratio) für SplitterDistance SplitPanelBuilder([System.String]$orientation, [double]$splitterRatio, [int]$panel1MinSize, [int]$panel2MinSize) { $this.SplitContainer = New-Object System.Windows.Forms.SplitContainer $this.SplitContainer.Dock = 'Fill' $this.SplitContainer.Orientation = $orientation # Speichere die gewünschten Werte $this.DesiredPanel1MinSize = $panel1MinSize $this.DesiredPanel2MinSize = $panel2MinSize $this.SplitterDistanceRatio = $splitterRatio # Setze nur die absolut notwendigen Anfangswerte $this.SplitContainer.Panel1MinSize = [Math]::Min($panel1MinSize, 25) $this.SplitContainer.Panel2MinSize = 25 } # Initialisiert den SplitContainer mit den finalen Werten (nach Form.Shown) [void] InitializeAfterShown([System.Windows.Forms.Form]$form) { # Bestimme verfügbare Breite/Höhe aus dem SplitContainer selbst $availableSize = if ($this.SplitContainer.Orientation -eq 'Vertical') { $this.SplitContainer.Width } else { $this.SplitContainer.Height } # Sicherheitscheck: Mindestgröße if ($availableSize -lt 100) { Write-Warning "SplitContainer zu klein: $availableSize px. Verwende ClientSize." $availableSize = if ($this.SplitContainer.Orientation -eq 'Vertical') { $form.ClientSize.Width } else { $form.ClientSize.Height } } # Panel2MinSize anpassen falls nötig $proposedPanel2 = $this.DesiredPanel2MinSize $maxAllowed = $availableSize - $this.DesiredPanel1MinSize - $this.SplitContainer.SplitterWidth if ($maxAllowed -lt 25) { # Nicht genug Platz - verwende Minimalkonfiguration $this.SplitContainer.Panel1MinSize = 25 $this.SplitContainer.Panel2MinSize = 25 $this.SplitContainer.SplitterDistance = 25 Write-Warning "Nicht genug Platz für gewünschte MinSizes. Verwende Minimalwerte." return } if ($proposedPanel2 -gt $maxAllowed) { $proposedPanel2 = [Math]::Max(25, [int]($maxAllowed / 2)) } # Panel1MinSize setzen $this.SplitContainer.Panel1MinSize = $this.DesiredPanel1MinSize # Panel2MinSize setzen $this.SplitContainer.Panel2MinSize = $proposedPanel2 # SplitterDistance berechnen und setzen if ($this.SplitterDistanceRatio -gt 0) { # Wenn Ratio angegeben wurde $desired = [int]($availableSize * $this.SplitterDistanceRatio) } else { # Wenn fester Wert angegeben wurde $desired = $this.DesiredSplitterDistance } # Validierung mit strengeren Grenzen $minAllowed = $this.SplitContainer.Panel1MinSize $maxAllowed = $availableSize - $this.SplitContainer.Panel2MinSize - $this.SplitContainer.SplitterWidth if ($desired -lt $minAllowed) { $desired = $minAllowed } if ($desired -gt $maxAllowed) { $desired = $maxAllowed } # Finale Sicherheitsprüfung if ($desired -lt 1) { $desired = $minAllowed } $this.SplitContainer.SplitterDistance = $desired } # Setzt Padding für Panel mit Berücksichtigung der Menühöhe [void] SetPanelPaddingWithMenu([int]$panelNumber, [System.Windows.Forms.MenuStrip]$menu, [int]$left, [int]$right, [int]$bottom) { try { $menuHeight = $menu.Height $topPadding = $menuHeight + 6 } catch { $topPadding = 36 # Fallback } $padding = New-Object System.Windows.Forms.Padding($left, $topPadding, $right, $bottom) if ($panelNumber -eq 1) { $this.SplitContainer.Panel1.Padding = $padding } else { $this.SplitContainer.Panel2.Padding = $padding } } [System.Windows.Forms.SplitterPanel] GetPanel1() { return $this.SplitContainer.Panel1 } # KORREKTUR: Gibt $this zurück für Fluent API [SplitPanelBuilder] AddToPanel1([System.Windows.Forms.Control]$control) { $this.SplitContainer.Panel1.Controls.Add($control) return $this } # KORREKTUR: Gibt $this zurück für Fluent API [SplitPanelBuilder] ClearPanel1() { $this.SplitContainer.Panel1.Controls.Clear() return $this } [System.Windows.Forms.SplitterPanel] GetPanel2() { return $this.SplitContainer.Panel2 } # KORREKTUR: Gibt $this zurück für Fluent API [SplitPanelBuilder] AddToPanel2([System.Windows.Forms.Control]$control) { $this.SplitContainer.Panel2.Controls.Add($control) return $this } # KORREKTUR: Gibt $this zurück für Fluent API [SplitPanelBuilder] ClearPanel2() { $this.SplitContainer.Panel2.Controls.Clear() return $this } # Berechnet die optimale SplitterDistance basierend auf den Controls in Panel1 [void] AutoSizeSplitterToPanel1Content() { $maxSize = 0 foreach ($control in $this.SplitContainer.Panel1.Controls) { if ($this.SplitContainer.Orientation -eq 'Vertical') { # Bei vertikaler Orientierung: Breite berechnen $requiredSize = $control.Right + $control.Margin.Right } else { # Bei horizontaler Orientierung: Höhe berechnen $requiredSize = $control.Bottom + $control.Margin.Bottom } if ($requiredSize -gt $maxSize) { $maxSize = $requiredSize } } # Padding von Panel1 berücksichtigen if ($this.SplitContainer.Orientation -eq 'Vertical') { $maxSize += $this.SplitContainer.Panel1.Padding.Right } else { $maxSize += $this.SplitContainer.Panel1.Padding.Bottom } # Sicherheitsabstand hinzufügen $maxSize += 10 # Mit Constraints validieren und setzen $this.SetSplitterDistance($maxSize) } # Setzt die Splitter-Position mit Validierung [SplitPanelBuilder] SetSplitterDistance([int]$distance) { $minDistance = $this.SplitContainer.Panel1MinSize $maxDistance = $this.SplitContainer.Width - $this.SplitContainer.Panel2MinSize if ($distance -lt $minDistance) { $this.SplitContainer.SplitterDistance = $minDistance } elseif ($distance -gt $maxDistance) { $this.SplitContainer.SplitterDistance = $maxDistance } else { $this.SplitContainer.SplitterDistance = $distance } return $this } # Fixiert den Splitter [SplitPanelBuilder] SetFixedPanel([System.String]$panel) { switch ($panel) { 'Panel1' { $this.SplitContainer.FixedPanel = 'Panel1' } 'Panel2' { $this.SplitContainer.FixedPanel = 'Panel2' } default { $this.SplitContainer.FixedPanel = 'None' } } return $this } # Verhindert das Verschieben des Splitters [SplitPanelBuilder] SetIsSplitterFixed([bool]$fixed) { $this.SplitContainer.IsSplitterFixed = $fixed return $this } # Setzt die Splitter-Breite [SplitPanelBuilder] SetSplitterWidth([int]$width) { $this.SplitContainer.SplitterWidth = $width return $this } # Gibt das SplitContainer-Objekt zurück [System.Windows.Forms.SplitContainer] GetSplitContainer() { return $this.SplitContainer } } class FlowLayoutPanelBuilder { [System.Windows.Forms.FlowLayoutPanel]$control FlowLayoutPanelBuilder() { $this.control = New-Object System.Windows.Forms.FlowLayoutPanel # Standard-Einstellungen $this.control.Dock = 'Fill' $this.control.AutoScroll = $true $this.control.FlowDirection = 'TopDown' $this.control.WrapContents = $false } [FlowLayoutPanelBuilder] SetDock([System.String]$dock) { $this.control.Dock = [System.Windows.Forms.DockStyle]::$dock return $this } [FlowLayoutPanelBuilder] SetAutoScroll([bool]$autoScroll) { $this.control.AutoScroll = $autoScroll return $this } [FlowLayoutPanelBuilder] SetFlowDirection([System.String]$direction) { $this.control.FlowDirection = [System.Windows.Forms.FlowDirection]::$direction return $this } [FlowLayoutPanelBuilder] SetWrapContents([bool]$wrap) { $this.control.WrapContents = $wrap return $this } [FlowLayoutPanelBuilder] SetPadding([int]$all) { $this.control.Padding = New-Object System.Windows.Forms.Padding($all) return $this } [FlowLayoutPanelBuilder] SetPadding([int]$left, [int]$top, [int]$right, [int]$bottom) { $this.control.Padding = New-Object System.Windows.Forms.Padding($left, $top, $right, $bottom) return $this } [FlowLayoutPanelBuilder] SetBackColor([System.Drawing.Color]$color) { $this.control.BackColor = $color return $this } [FlowLayoutPanelBuilder] SetWidth([int]$width) { $this.control.Width = $width return $this } [FlowLayoutPanelBuilder] SetHeight([int]$height) { $this.control.Height = $height return $this } [FlowLayoutPanelBuilder] SetSize([int]$width, [int]$height) { $this.control.Width = $width $this.control.Height = $height return $this } [FlowLayoutPanelBuilder] SetBorderStyle([System.String]$style) { $this.control.BorderStyle = [System.Windows.Forms.BorderStyle]::$style return $this } [FlowLayoutPanelBuilder] AddControl([System.Windows.Forms.Control]$childControl) { $this.control.Controls.Add($childControl) return $this } [FlowLayoutPanelBuilder] AddControls([System.Windows.Forms.Control[]]$controls) { foreach ($ctrl in $controls) { $this.control.Controls.Add($ctrl) } return $this } [FlowLayoutPanelBuilder] ClearControls() { $this.control.Controls.Clear() return $this } [System.Windows.Forms.FlowLayoutPanel] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.control) return $this.control } [System.Windows.Forms.FlowLayoutPanel] Build() { return $this.control } } class ButtonBuilder : BaseComponent { [System.Windows.Forms.Button] $Button [scriptblock] $EnableCondition = $null [StateManager] $StateManager = $null ButtonBuilder() { $this.Button = New-Object System.Windows.Forms.Button $this.Button.Font = New-Object System.Drawing.Font("Segoe UI", $this.GetSetting("FontSize")) $this.SetSize(40, 40) $this.Button.Margin = New-Object System.Windows.Forms.Padding(5) } ButtonBuilder([System.String]$text) { $this.Button = New-Object System.Windows.Forms.Button $this.Button.Text = $text $this.Button.Font = New-Object System.Drawing.Font("Segoe UI", $this.GetSetting("FontSize")) $this.SetSize(40, 40) } # Konstruktor mit Text und Höhe ButtonBuilder([System.String]$text, [int]$height) { $this.Button = New-Object System.Windows.Forms.Button $this.Button.Text = $text $this.Button.Height = $height $this.Button.Font = New-Object System.Drawing.Font("Segoe UI", $this.GetSetting("FontSize")) } # Setzt den Text [ButtonBuilder] SetText([System.String]$text) { $this.Button.Text = $text return $this } # Setzt die Höhe [ButtonBuilder] SetHeight([int]$height) { $this.Button.Height = $height return $this } # Setzt die Breite [ButtonBuilder] SetWidth([int]$width) { $this.Button.Width = $width return $this } # Setzt die Größe [ButtonBuilder] SetSize([int]$width, [int]$height) { $this.Button.Size = New-Object System.Drawing.Size($width, $height) return $this } # Setzt das Dock [ButtonBuilder] SetDock([System.String]$dock) { $this.Button.Dock = $dock return $this } # Setzt die Position [ButtonBuilder] SetLocation([int]$x, [int]$y) { $this.Button.Location = New-Object System.Drawing.Point($x, $y) return $this } # Setzt die Schriftart [ButtonBuilder] SetFont([System.String]$fontFamily, [int]$fontSize) { $this.Button.Font = New-Object System.Drawing.Font($fontFamily, $fontSize) return $this } # Setzt die Schriftart mit Stil [ButtonBuilder] SetFont([System.String]$fontFamily, [int]$fontSize, [System.Drawing.FontStyle]$fontStyle) { $this.Button.Font = New-Object System.Drawing.Font($fontFamily, $fontSize, $fontStyle) return $this } # Setzt die Hintergrundfarbe [ButtonBuilder] SetBackColor([System.Drawing.Color]$color) { $this.Button.BackColor = $color return $this } # Setzt die Textfarbe [ButtonBuilder] SetForeColor([System.Drawing.Color]$color) { $this.Button.ForeColor = $color return $this } # Setzt den Enabled-Status [ButtonBuilder] SetEnabled([bool]$enabled) { $this.Button.Enabled = $enabled return $this } # Setzt die Sichtbarkeit [ButtonBuilder] SetVisible([bool]$visible) { $this.Button.Visible = $visible return $this } # Setzt das FlatStyle [ButtonBuilder] SetFlatStyle([System.String]$style) { switch ($style) { 'Flat' { $this.Button.FlatStyle = 'Flat' } 'Popup' { $this.Button.FlatStyle = 'Popup' } 'Standard' { $this.Button.FlatStyle = 'Standard' } 'System' { $this.Button.FlatStyle = 'System' } } return $this } # Setzt ein MDL2-Icon [ButtonBuilder] SetMDL2Icon([System.String]$unicodeChar) { $this.Button.Font = New-Object System.Drawing.Font("Segoe MDL2 Assets", 14) $this.Button.Text = $unicodeChar return $this } # Setzt ein Fluent-Icon [ButtonBuilder] SetFluentIcon([System.String]$unicodeChar) { $this.Button.Font = New-Object System.Drawing.Font("Segoe Fluent Icons", 14) $this.Button.Text = $unicodeChar return $this } # NEU: Bindet Button an StateManager (ohne Bedingung) [ButtonBuilder] WithStateManager([StateManager]$stateManager) { $this.StateManager = $stateManager return $this } # NEU: Kombinierte Methode - Bedingung + StateManager [ButtonBuilder] BindEnabled([scriptblock]$condition, [StateManager]$stateManager) { $this.EnableCondition = $condition $this.StateManager = $stateManager $this.UpdateEnabledState() return $this } # Original-Methode - nur Bedingung, kein StateManager [ButtonBuilder] BindEnabled([scriptblock]$condition) { $this.EnableCondition = $condition $this.UpdateEnabledState() return $this } # Aktualisiert den Enabled-Status basierend auf der Bedingung [void] UpdateEnabledState() { if ($null -ne $this.EnableCondition) { try { $result = & $this.EnableCondition $this.Button.Enabled = [bool]$result } catch { Write-Warning "Fehler beim Evaluieren der Enable-Bedingung: $_" $this.Button.Enabled = $false } } } # Setzt den Click-Handler [ButtonBuilder] SetClickHandler([scriptblock]$handler) { $this.Button.Add_Click($handler) return $this } # Setzt einen Tooltip [ButtonBuilder] SetToolTip([System.String]$text) { $tooltip = New-Object System.Windows.Forms.ToolTip $tooltip.SetToolTip($this.Button, $text) return $this } # Setzt das Anchor [ButtonBuilder] SetAnchor([System.Windows.Forms.AnchorStyles]$anchor) { $this.Button.Anchor = $anchor return $this } # Gibt das Button-Objekt zurück [System.Windows.Forms.Button] GetButton() { return $this.Button } # Fügt den Button zu einem Control hinzu [void] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.Button) } # NEU: Build mit Auto-Registrierung beim StateManager [System.Windows.Forms.Button] Build() { # Auto-Registrierung wenn StateManager gesetzt if ($null -ne $this.StateManager -and $null -ne $this.EnableCondition) { $this.StateManager.RegisterButton($this) } return $this.Button } } class LabelBuilder : BaseComponent { [System.Windows.Forms.Label] $Label LabelBuilder([System.String]$text) { $this.Label = New-Object System.Windows.Forms.Label $this.Label.Text = $text $this.Label.Font = New-Object System.Drawing.Font("Segoe UI", $this.GetSetting("FontSize")) } # Konstruktor mit Text und Höhe LabelBuilder([System.String]$text, [int]$height) { $this.Label = New-Object System.Windows.Forms.Label $this.Label.Text = $text $this.Label.Height = $height $this.Label.Font = New-Object System.Drawing.Font("Segoe UI", $this.GetSetting("FontSize")) } # Setzt den Text [LabelBuilder] SetText([System.String]$text) { $this.Label.Text = $text return $this } # Setzt die Höhe [LabelBuilder] SetHeight([int]$height) { $this.Label.Height = $height return $this } # Setzt die Breite [LabelBuilder] SetWidth([int]$width) { $this.Label.Width = $width return $this } # Setzt die Größe [LabelBuilder] SetSize([int]$width, [int]$height) { $this.Label.Size = New-Object System.Drawing.Size($width, $height) return $this } # Setzt das Dock [LabelBuilder] SetDock([System.String]$dock) { $this.Label.Dock = $dock return $this } # Setzt die Position [LabelBuilder] SetLocation([int]$x, [int]$y) { $this.Label.Location = New-Object System.Drawing.Point($x, $y) return $this } # Setzt die Schriftart [LabelBuilder] SetFont([System.String]$fontFamily, [int]$fontSize) { $this.Label.Font = New-Object System.Drawing.Font($fontFamily, $fontSize) return $this } # Setzt die Schriftart mit Stil [LabelBuilder] SetFont([System.String]$fontFamily, [int]$fontSize, [System.Drawing.FontStyle]$fontStyle) { $this.Label.Font = New-Object System.Drawing.Font($fontFamily, $fontSize, $fontStyle) return $this } # Setzt die Hintergrundfarbe [LabelBuilder] SetBackColor([System.Drawing.Color]$color) { $this.Label.BackColor = $color return $this } # Setzt die Textfarbe [LabelBuilder] SetForeColor([System.Drawing.Color]$color) { $this.Label.ForeColor = $color return $this } # Setzt die Textausrichtung [LabelBuilder] SetTextAlign([System.Drawing.ContentAlignment]$alignment) { $this.Label.TextAlign = $alignment return $this } # Setzt AutoSize [LabelBuilder] SetAutoSize([bool]$autoSize) { $this.Label.AutoSize = $autoSize return $this } # Setzt den Enabled-Status [LabelBuilder] SetEnabled([bool]$enabled) { $this.Label.Enabled = $enabled return $this } # Setzt die Sichtbarkeit [LabelBuilder] SetVisible([bool]$visible) { $this.Label.Visible = $visible return $this } # Setzt BorderStyle [LabelBuilder] SetBorderStyle([System.String]$style) { switch ($style) { 'None' { $this.Label.BorderStyle = 'None' } 'FixedSingle' { $this.Label.BorderStyle = 'FixedSingle' } 'Fixed3D' { $this.Label.BorderStyle = 'Fixed3D' } } return $this } # Setzt das Padding [LabelBuilder] SetPadding([int]$left, [int]$top, [int]$right, [int]$bottom) { $this.Label.Padding = New-Object System.Windows.Forms.Padding($left, $top, $right, $bottom) return $this } # Setzt das Margin [LabelBuilder] SetMargin([int]$left, [int]$top, [int]$right, [int]$bottom) { $this.Label.Margin = New-Object System.Windows.Forms.Padding($left, $top, $right, $bottom) return $this } # Setzt das Padding (alle Seiten gleich) [LabelBuilder] SetPadding([int]$all) { $this.Label.Padding = New-Object System.Windows.Forms.Padding($all) return $this } # Setzt das Anchor [LabelBuilder] SetAnchor([System.Windows.Forms.AnchorStyles]$anchor) { $this.Label.Anchor = $anchor return $this } # Setzt einen Tooltip [LabelBuilder] SetToolTip([System.String]$text) { $tooltip = New-Object System.Windows.Forms.ToolTip $tooltip.SetToolTip($this.Label, $text) return $this } # Setzt das UseMnemonic (für & Zeichen) [LabelBuilder] SetUseMnemonic([bool]$useMnemonic) { $this.Label.UseMnemonic = $useMnemonic return $this } # Gibt das Label-Objekt zurück [System.Windows.Forms.Label] GetLabel() { return $this.Label } # Fügt das Label zu einem Control hinzu [void] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.Label) } [System.Windows.Forms.Label] Build() { return $this.Label } } class TreeViewBuilder : BaseComponent { [System.Windows.Forms.TreeView]$control TreeViewBuilder() { $this.control = New-Object System.Windows.Forms.TreeView # Standard-Einstellungen $this.control.Dock = 'Fill' $this.control.HideSelection = $false $this.control.ShowPlusMinus = $true $this.control.ShowRootLines = $true $this.control.FullRowSelect = $true } [TreeViewBuilder] SetDock([System.String]$dock) { $this.control.Dock = [System.Windows.Forms.DockStyle]::$dock return $this } [TreeViewBuilder] SetFont([System.String]$family, [float]$size) { $this.control.Font = New-Object System.Drawing.Font($family, $size) return $this } [TreeViewBuilder] SetFont([System.String]$family, [float]$size, [System.Drawing.FontStyle]$style) { $this.control.Font = New-Object System.Drawing.Font($family, $size, $style) return $this } [TreeViewBuilder] SetHideSelection([bool]$hide) { $this.control.HideSelection = $hide return $this } [TreeViewBuilder] SetShowPlusMinus([bool]$show) { $this.control.ShowPlusMinus = $show return $this } [TreeViewBuilder] SetShowRootLines([bool]$show) { $this.control.ShowRootLines = $show return $this } [TreeViewBuilder] SetFullRowSelect([bool]$select) { $this.control.FullRowSelect = $select return $this } [TreeViewBuilder] SetShowLines([bool]$show) { $this.control.ShowLines = $show return $this } [TreeViewBuilder] SetCheckBoxes([bool]$show) { $this.control.CheckBoxes = $show return $this } [TreeViewBuilder] SetImageList([System.Windows.Forms.ImageList]$imageList) { $this.control.ImageList = $imageList return $this } [TreeViewBuilder] SetBackColor([System.Drawing.Color]$color) { $this.control.BackColor = $color return $this } [TreeViewBuilder] SetForeColor([System.Drawing.Color]$color) { $this.control.ForeColor = $color return $this } [TreeViewBuilder] AddNode([System.String]$name) { $this.control.Nodes.Add([System.Windows.Forms.TreeNode]::new($name)) | Out-Null return $this } [TreeViewBuilder] AddNode([System.Windows.Forms.TreeNode]$node) { $this.control.Nodes.Add($node) | Out-Null return $this } [TreeViewBuilder] AddNodes([System.Windows.Forms.TreeNode[]]$nodes) { foreach ($node in $nodes) { $this.control.Nodes.Add($node) | Out-Null } return $this } # NEU: Lädt Hashtable/Template in TreeView [TreeViewBuilder] LoadTemplate([object]$template, [System.String]$rootName = "Template") { $this.control.Nodes.Clear() $rootNode = $this.control.Nodes.Add($rootName) try { if ($template -is [System.Collections.IDictionary]) { # WICHTIG: Keine Sortierung, Keys in Original-Reihenfolge foreach ($key in $template.Keys) { $this.AddTreeNodeRecursive($rootNode, $template[$key], $key, "") } } else { $this.AddTreeNodeRecursive($rootNode, $template, $rootName, "") } } catch { $rootNode.Nodes.Add("Fehler beim Lesen: $($_.Exception.Message)") | Out-Null } $rootNode.ExpandAll() return $this } # NEU: Rekursive Methode zum Hinzufügen von Nodes hidden [void] AddTreeNodeRecursive([System.Windows.Forms.TreeNode]$parentNode, [object]$value, [System.String]$name, [System.String]$path) { $currentPath = if ($path -eq "") { $name } else { "$path.$name" } if ($value -is [System.Collections.IDictionary]) { $node = $parentNode.Nodes.Add($name, $name) $node.Tag = @{ Type = "Hashtable"; Path = $currentPath; Value = $value } # WICHTIG: KEIN Sort-Object! Behält die Original-Reihenfolge bei foreach ($k in $value.Keys) { $this.AddTreeNodeRecursive($node, $value[$k], $k, $currentPath) } } elseif ($value -is [System.Collections.IEnumerable] -and -not ($value -is [string])) { $node = $parentNode.Nodes.Add($name, $name) $node.Tag = @{ Type = "Array"; Path = $currentPath; Value = $value } $i = 0 foreach ($item in $value) { $this.AddTreeNodeRecursive($node, $item, "[$i]", $currentPath) $i++ } } else { $displayText = "$name = $value" $node = $parentNode.Nodes.Add($name, $displayText) $node.Tag = @{ Type = "Value"; Path = $currentPath; Key = $name; Value = $value } } } # NEU: Löscht alle Nodes [TreeViewBuilder] ClearNodes() { $this.control.Nodes.Clear() return $this } [TreeViewBuilder] AddEventHandler([System.String]$eventName, [scriptblock]$handler) { $this.control."Add_$eventName"($handler) return $this } [System.Windows.Forms.TreeView] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.control) return $this.control } [System.Windows.Forms.TreeView] Build() { return $this.control } } class DataGridViewBuilder : BaseComponent{ [System.Windows.Forms.DataGridView] $control DataGridViewBuilder() { $this.control = New-Object System.Windows.Forms.DataGridView $this.control.Dock = "Fill" $this.control.ColumnHeadersHeight = 30 $this.control.AutoSizeColumnsMode = "Fill" } [DataGridViewBuilder] SetAllowAddRows([Boolean] $enabled) { $this.control.AllowUserToAddRows = $enabled return $this } [DataGridViewBuilder] SetHeaderColumns([System.Array] $Columns) { foreach($Column in $Columns){ $this.control.Columns.Add($Column,$Column) } return $this } [DataGridViewBuilder] SetHeaderColumnReadOnly([System.String] $Name) { $this.control.Columns[$Name].ReadOnly = $true return $this } [System.Windows.Forms.DataGridView] Build() { return $this.control } } class GroupBoxBuilder : BaseComponent { [System.Windows.Forms.GroupBox]$control [bool]$autoSizeToContent = $false GroupBoxBuilder([System.String]$text) { $this.control = New-Object System.Windows.Forms.GroupBox $this.control.Text = $text $this.control.Font = New-Object System.Drawing.Font("Segoe UI", $this.GetSetting("FontSize"),[System.Drawing.FontStyle]::Bold) #$this.control.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowOnly } [GroupBoxBuilder] SetText([System.String]$text) { $this.control.Text = $text return $this } [GroupBoxBuilder] SetWidth([int]$width) { $this.control.Width = $width return $this } [GroupBoxBuilder] SetHeight([int]$height) { $this.control.Height = $height return $this } [GroupBoxBuilder] SetSize([int]$width, [int]$height) { $this.control.Width = $width $this.control.Height = $height return $this } [GroupBoxBuilder] SetFont([System.String]$family, [float]$size) { $this.control.Font = New-Object System.Drawing.Font($family, $size) return $this } [GroupBoxBuilder] SetFont([System.String]$family, [float]$size, [System.Drawing.FontStyle]$style) { $this.control.Font = New-Object System.Drawing.Font($family, $size, $style) return $this } [GroupBoxBuilder] SetMargin([int]$all) { $this.control.Margin = New-Object System.Windows.Forms.Padding($all) return $this } [GroupBoxBuilder] SetMargin([int]$left, [int]$top, [int]$right, [int]$bottom) { $this.control.Margin = New-Object System.Windows.Forms.Padding($left, $top, $right, $bottom) return $this } [GroupBoxBuilder] SetPadding([int]$all) { $this.control.Padding = New-Object System.Windows.Forms.Padding($all) return $this } [GroupBoxBuilder] SetPadding([int]$left, [int]$top, [int]$right, [int]$bottom) { $this.control.Padding = New-Object System.Windows.Forms.Padding($left, $top, $right, $bottom) return $this } [GroupBoxBuilder] SetDock([System.String]$dock) { $this.control.Dock = [System.Windows.Forms.DockStyle]::$dock return $this } [GroupBoxBuilder] SetBackColor([System.Drawing.Color]$color) { $this.control.BackColor = $color return $this } [GroupBoxBuilder] SetForeColor([System.Drawing.Color]$color) { $this.control.ForeColor = $color return $this } [GroupBoxBuilder] SetAutoSize([bool]$autoSize) { $this.control.AutoSize = $autoSize return $this } [GroupBoxBuilder] SetAutoSizeMode([System.String]$autoSizeMode) { $this.control.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::$autoSizeMode return $this } [GroupBoxBuilder] AddControl([System.Windows.Forms.Control]$childControl) { $this.control.Controls.Add($childControl) return $this } [GroupBoxBuilder] AddControls([System.Windows.Forms.Control[]]$controls) { foreach ($ctrl in $controls) { $this.control.Controls.Add($ctrl) } return $this } [System.Windows.Forms.GroupBox] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.control) return $this.control } [System.Windows.Forms.GroupBox] Build() { return $this.control } } class ListBoxBuilder : BaseComponent { [System.Windows.Forms.ListBox]$control ListBoxBuilder() { $this.control = New-Object System.Windows.Forms.ListBox # Standard-Einstellungen $this.control.DisplayMember = 'BaseName' $this.control.ValueMember = 'FullName' $this.control.Dock = 'Fill' $this.control.Font = New-Object System.Drawing.Font("Segoe UI", $this.GetSetting("FontSize")) } [ListBoxBuilder] SetDock([System.String]$dock) { $this.control.Dock = [System.Windows.Forms.DockStyle]::$dock return $this } [ListBoxBuilder] SetFont([System.String]$family, [float]$size) { $this.control.Font = New-Object System.Drawing.Font($family, $size) return $this } [ListBoxBuilder] SetFont([System.String]$family, [float]$size, [System.Drawing.FontStyle]$style) { $this.control.Font = New-Object System.Drawing.Font($family, $size, $style) return $this } [ListBoxBuilder] SetSelectionMode([System.String]$mode) { $this.control.SelectionMode = [System.Windows.Forms.SelectionMode]::$mode return $this } [ListBoxBuilder] SetBackColor([System.Drawing.Color]$color) { $this.control.BackColor = $color return $this } [ListBoxBuilder] SetForeColor([System.Drawing.Color]$color) { $this.control.ForeColor = $color return $this } [ListBoxBuilder] SetBorderStyle([System.String]$style) { $this.control.BorderStyle = [System.Windows.Forms.BorderStyle]::$style return $this } [ListBoxBuilder] SetHorizontalScrollbar([bool]$show) { $this.control.HorizontalScrollbar = $show return $this } [ListBoxBuilder] SetIntegralHeight([bool]$integral) { $this.control.IntegralHeight = $integral return $this } [ListBoxBuilder] SetSorted([bool]$sorted) { $this.control.Sorted = $sorted return $this } [ListBoxBuilder] SetHeight([Int]$height) { $this.control.Height = $height return $this } [ListBoxBuilder] AddItem([object]$item) { $this.control.Items.Add($item) | Out-Null return $this } [ListBoxBuilder] AddItems([object[]]$items) { foreach ($item in $items) { $this.control.Items.Add($item) | Out-Null } return $this } [ListBoxBuilder] ClearItems() { $this.control.Items.Clear() return $this } [ListBoxBuilder] SetDataSource([object]$dataSource) { $this.control.DataSource = $dataSource return $this } [ListBoxBuilder] SetDisplayMember([System.String]$member) { $this.control.DisplayMember = $member return $this } [ListBoxBuilder] SetValueMember([System.String]$member) { $this.control.ValueMember = $member return $this } [ListBoxBuilder] AddSelectedIndexChangedHandler([scriptblock]$handler) { $this.control.Add_SelectedIndexChanged($handler) return $this } [ListBoxBuilder] AddMouseDoubleClickHandler([scriptblock]$handler) { $this.control.Add_MouseDoubleClick($handler) return $this } [ListBoxBuilder] AddEventHandler([System.String]$eventName, [scriptblock]$handler) { $this.control."Add_$eventName"($handler) return $this } [System.Windows.Forms.ListBox] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.control) return $this.control } [System.Windows.Forms.ListBox] Build() { return $this.control } } class PanelBuilder : BaseComponent{ [System.Windows.Forms.Panel]$control [scriptblock] $EnableCondition = $null [StateManager] $StateManager = $null PanelBuilder() { $this.control = New-Object System.Windows.Forms.Panel } [PanelBuilder] SetDock([System.String]$dock) { $this.control.Dock = [System.Windows.Forms.DockStyle]::$dock return $this } [PanelBuilder] SetWidth([int]$width) { $this.control.Width = $width return $this } [PanelBuilder] SetHeight([int]$height) { $this.control.Height = $height return $this } [PanelBuilder] SetSize([int]$width, [int]$height) { $this.control.Size = New-Object System.Drawing.Size($width, $height) return $this } [PanelBuilder] SetLocation([int]$x, [int]$y) { $this.control.Location = New-Object System.Drawing.Point($x, $y) return $this } [PanelBuilder] SetBackColor([System.Drawing.Color]$color) { $this.control.BackColor = $color return $this } [PanelBuilder] SetForeColor([System.Drawing.Color]$color) { $this.control.ForeColor = $color return $this } [PanelBuilder] SetBorderStyle([System.String]$style) { $this.control.BorderStyle = [System.Windows.Forms.BorderStyle]::$style return $this } [PanelBuilder] SetAutoScroll([bool]$autoScroll) { $this.control.AutoScroll = $autoScroll return $this } [PanelBuilder] SetPadding([int]$all) { $this.control.Padding = New-Object System.Windows.Forms.Padding($all) return $this } [PanelBuilder] SetPadding([int]$left, [int]$top, [int]$right, [int]$bottom) { $this.control.Padding = New-Object System.Windows.Forms.Padding($left, $top, $right, $bottom) return $this } [PanelBuilder] SetMargin([int]$all) { $this.control.Margin = New-Object System.Windows.Forms.Padding($all) return $this } [PanelBuilder] SetMargin([int]$left, [int]$top, [int]$right, [int]$bottom) { $this.control.Margin = New-Object System.Windows.Forms.Padding($left, $top, $right, $bottom) return $this } [PanelBuilder] SetAnchor([System.Windows.Forms.AnchorStyles]$anchor) { $this.control.Anchor = $anchor return $this } [PanelBuilder] SetVisible([bool]$visible) { $this.control.Visible = $visible return $this } [PanelBuilder] SetEnabled([bool]$enabled) { $this.control.Enabled = $enabled return $this } [PanelBuilder] SetAutoSize([bool]$autoSize) { $this.control.AutoSize = $autoSize return $this } [PanelBuilder] SetAutoSizeMode([System.String]$mode) { $this.control.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::$mode return $this } [PanelBuilder] AddControl([System.Windows.Forms.Control]$childControl) { $this.control.Controls.Add($childControl) return $this } [PanelBuilder] AddControls([System.Windows.Forms.Control[]]$controls) { foreach ($ctrl in $controls) { $this.control.Controls.Add($ctrl) } return $this } [PanelBuilder] ClearControls() { $this.control.Controls.Clear() return $this } [PanelBuilder] AddEventHandler([System.String]$eventName, [scriptblock]$handler) { $this.control."Add_$eventName"($handler) return $this } [PanelBuilder] SetName([System.String]$name) { $this.control.Name = $name return $this } [PanelBuilder] SetTag([object]$tag) { $this.control.Tag = $tag return $this } [System.Windows.Forms.Panel] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.control) return $this.control } [PanelBuilder] BindEnabled([scriptblock]$condition, [StateManager]$stateManager) { $this.EnableCondition = $condition $this.StateManager = $stateManager $this.UpdateVisibleState() return $this } # Aktualisiert den Visible-Status basierend auf der Bedingung [void] UpdateVisibleState() { if ($null -ne $this.EnableCondition) { try { $result = & $this.EnableCondition $this.control.Visible = [bool]$result } catch { Write-Warning "Fehler beim Evaluieren der Visible-Bedingung: $_" $this.control.Visible = $false } } } [System.Windows.Forms.Panel] Build() { if ($null -ne $this.StateManager -and $null -ne $this.EnableCondition) { $this.StateManager.RegisterForm($this) } return $this.control } [System.Windows.Forms.Panel] GetPanel() { return $this.control } } class DialogBuilder : BaseComponent { [object] $Dialog # Kann FileDialog oder FolderBrowserDialog sein [System.String] $DialogType # Privater Konstruktor hidden DialogBuilder([System.String]$type) { $this.DialogType = $type switch ($type) { "Open" { $this.Dialog = New-Object System.Windows.Forms.OpenFileDialog $this.Dialog.Multiselect = $false $this.Dialog.Filter = "Alle Dateien (*.*)|*.*" $this.Dialog.FilterIndex = 1 $this.Dialog.RestoreDirectory = $true } "Save" { $this.Dialog = New-Object System.Windows.Forms.SaveFileDialog $this.Dialog.OverwritePrompt = $true $this.Dialog.Filter = "Alle Dateien (*.*)|*.*" $this.Dialog.FilterIndex = 1 $this.Dialog.RestoreDirectory = $true } "Folder" { $this.Dialog = New-Object System.Windows.Forms.FolderBrowserDialog $this.Dialog.ShowNewFolderButton = $true } } } # === FACTORY-METHODEN === static [DialogBuilder] OpenFile() { return [DialogBuilder]::new("Open") } static [DialogBuilder] SaveFile() { return [DialogBuilder]::new("Save") } static [DialogBuilder] SelectFolder() { return [DialogBuilder]::new("Folder") } # === GEMEINSAME METHODEN (FileDialog) === # Setzt den Titel [DialogBuilder] SetTitle([System.String]$title) { if ($this.DialogType -eq "Folder") { $this.Dialog.Description = $title } else { $this.Dialog.Title = $title } return $this } # Setzt den Filter (nur File-Dialoge) [DialogBuilder] SetFilter([System.String]$filter) { if ($this.DialogType -ne "Folder") { $this.Dialog.Filter = $filter } else { Write-Warning "Filter ist nur für File-Dialoge verfügbar" } return $this } # Setzt den aktiven Filter-Index (nur File-Dialoge) [DialogBuilder] SetFilterIndex([int]$index) { if ($this.DialogType -ne "Folder") { $this.Dialog.FilterIndex = $index } return $this } # Setzt das Initial-Verzeichnis [DialogBuilder] SetInitialDirectory([System.String]$path) { if ($this.DialogType -eq "Folder") { if (Test-Path $path) { $this.Dialog.SelectedPath = $path } else { Write-Warning "Pfad existiert nicht: $path" } } else { if (Test-Path $path) { $this.Dialog.InitialDirectory = $path } else { Write-Warning "Pfad existiert nicht: $path" } } return $this } # Setzt den Standard-Dateinamen (nur File-Dialoge) [DialogBuilder] SetFileName([System.String]$fileName) { if ($this.DialogType -ne "Folder") { $this.Dialog.FileName = $fileName } return $this } # Prüft ob Datei existieren muss (nur File-Dialoge) [DialogBuilder] SetCheckFileExists([bool]$check) { if ($this.DialogType -ne "Folder") { $this.Dialog.CheckFileExists = $check } return $this } # Prüft ob Pfad existieren muss (nur File-Dialoge) [DialogBuilder] SetCheckPathExists([bool]$check) { if ($this.DialogType -ne "Folder") { $this.Dialog.CheckPathExists = $check } return $this } # Wiederherstellt letztes Verzeichnis (nur File-Dialoge) [DialogBuilder] SetRestoreDirectory([bool]$restore) { if ($this.DialogType -ne "Folder") { $this.Dialog.RestoreDirectory = $restore } return $this } # Validiert Namen gegen ungültige Zeichen (nur File-Dialoge) [DialogBuilder] SetValidateNames([bool]$validate) { if ($this.DialogType -ne "Folder") { $this.Dialog.ValidateNames = $validate } return $this } # Dereferenziert Links/Shortcuts (nur File-Dialoge) [DialogBuilder] SetDereferenceLinks([bool]$dereference) { if ($this.DialogType -ne "Folder") { $this.Dialog.DereferenceLinks = $dereference } return $this } # Setzt die Standard-Erweiterung (nur File-Dialoge) [DialogBuilder] SetDefaultExt([System.String]$ext) { if ($this.DialogType -ne "Folder") { $this.Dialog.DefaultExt = $ext.TrimStart('.') } return $this } # Fügt automatisch Erweiterung hinzu (nur File-Dialoge) [DialogBuilder] SetAddExtension([bool]$add) { if ($this.DialogType -ne "Folder") { $this.Dialog.AddExtension = $add } return $this } # Setzt Custom Places (nur File-Dialoge) [DialogBuilder] AddCustomPlace([System.String]$path) { if ($this.DialogType -ne "Folder") { if (Test-Path $path) { $this.Dialog.CustomPlaces.Add($path) } } return $this } # Setzt mehrere Custom Places (nur File-Dialoge) [DialogBuilder] AddCustomPlaces([System.String[]]$paths) { if ($this.DialogType -ne "Folder") { foreach ($path in $paths) { if (Test-Path $path) { $this.Dialog.CustomPlaces.Add($path) } } } return $this } # === OPENFILEDIALOG-SPEZIFISCH === # Aktiviert/Deaktiviert Mehrfachauswahl [DialogBuilder] SetMultiselect([bool]$multiselect) { if ($this.DialogType -eq "Open") { $this.Dialog.Multiselect = $multiselect } else { Write-Warning "Multiselect ist nur für OpenFileDialog verfügbar" } return $this } # Zeigt Read-Only Checkbox [DialogBuilder] SetShowReadOnly([bool]$show) { if ($this.DialogType -eq "Open") { $this.Dialog.ShowReadOnly = $show } return $this } # Setzt Read-Only Status [DialogBuilder] SetReadOnlyChecked([bool]$checked) { if ($this.DialogType -eq "Open") { $this.Dialog.ReadOnlyChecked = $checked } return $this } # === SAVEFILEDIALOG-SPEZIFISCH === # Warnt bei Überschreiben [DialogBuilder] SetOverwritePrompt([bool]$prompt) { if ($this.DialogType -eq "Save") { $this.Dialog.OverwritePrompt = $prompt } return $this } # Warnt beim Erstellen neuer Dateien [DialogBuilder] SetCreatePrompt([bool]$prompt) { if ($this.DialogType -eq "Save") { $this.Dialog.CreatePrompt = $prompt } return $this } # === FOLDERBROWSERDIALOG-SPEZIFISCH === # Zeigt "Neuer Ordner"-Button [DialogBuilder] SetShowNewFolderButton([bool]$show) { if ($this.DialogType -eq "Folder") { $this.Dialog.ShowNewFolderButton = $show } return $this } # Setzt die Beschreibung (Folder-spezifisch) [DialogBuilder] SetDescription([System.String]$description) { if ($this.DialogType -eq "Folder") { $this.Dialog.Description = $description } return $this } # Setzt den Root-Folder (Folder-spezifisch) [DialogBuilder] SetRootFolder([System.String]$specialFolder) { if ($this.DialogType -eq "Folder") { try { $this.Dialog.RootFolder = [System.Environment+SpecialFolder]::$specialFolder } catch { Write-Warning "Ungültiger SpecialFolder: $specialFolder" } } return $this } # Setzt den ausgewählten Pfad (Folder-spezifisch) [DialogBuilder] SetSelectedPath([System.String]$path) { if ($this.DialogType -eq "Folder") { if (Test-Path $path) { $this.Dialog.SelectedPath = $path } else { Write-Warning "Pfad existiert nicht: $path" } } return $this } # Verwendet die neue Folder-Browser-Darstellung (Vista-Style) [DialogBuilder] SetUseDescriptionForTitle([bool]$use) { if ($this.DialogType -eq "Folder") { $this.Dialog.UseDescriptionForTitle = $use } return $this } # === VOREINGESTELLTE FILTER === # Schnell-Methode für häufige Filter (nur File-Dialoge) [DialogBuilder] SetCommonFilter([System.String]$type) { if ($this.DialogType -eq "Folder") { Write-Warning "Filter sind für FolderBrowserDialog nicht verfügbar" return $this } switch ($type) { 'Text' { $this.Dialog.Filter = "Text-Dateien (*.txt)|*.txt|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "txt" } 'PowerShell' { $this.Dialog.Filter = "PowerShell-Dateien (*.ps1;*.psm1;*.psd1)|*.ps1;*.psm1;*.psd1|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "ps1" } 'PSD1' { $this.Dialog.Filter = "PowerShell Data-Dateien (*.psd1)|*.psd1|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "psd1" } 'JSON' { $this.Dialog.Filter = "JSON-Dateien (*.json)|*.json|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "json" } 'XML' { $this.Dialog.Filter = "XML-Dateien (*.xml)|*.xml|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "xml" } 'CSV' { $this.Dialog.Filter = "CSV-Dateien (*.csv)|*.csv|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "csv" } 'Excel' { $this.Dialog.Filter = "Excel-Dateien (*.xlsx;*.xls)|*.xlsx;*.xls|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "xlsx" } 'Image' { $this.Dialog.Filter = "Bild-Dateien (*.png;*.jpg;*.jpeg;*.gif;*.bmp)|*.png;*.jpg;*.jpeg;*.gif;*.bmp|Alle Dateien (*.*)|*.*" $this.Dialog.DefaultExt = "png" } 'All' { $this.Dialog.Filter = "Alle Dateien (*.*)|*.*" } default { Write-Warning "Unbekannter Filter-Typ: $type" } } return $this } # === SHOW UND BUILD === # Zeigt den Dialog und gibt das Ergebnis zurück [System.Collections.Hashtable] Show() { $result = @{ Result = $false FileName = $null FileNames = @() SafeFileName = $null SafeFileNames = @() SelectedPath = $null } $dialogResult = $this.Dialog.ShowDialog() if ($dialogResult -eq 'OK') { $result.Result = $true if ($this.DialogType -eq "Folder") { $result.SelectedPath = $this.Dialog.SelectedPath $result.FileName = $this.Dialog.SelectedPath } else { $result.FileName = $this.Dialog.FileName $result.SafeFileName = $this.Dialog.SafeFileName if ($this.DialogType -eq "Open") { $result.FileNames = $this.Dialog.FileNames $result.SafeFileNames = $this.Dialog.SafeFileNames } else { $result.FileNames = @($this.Dialog.FileName) $result.SafeFileNames = @($this.Dialog.SafeFileName) } } } return $result } # Zeigt den Dialog mit Custom Parent Window [System.Collections.Hashtable] Show([System.Windows.Forms.IWin32Window]$owner) { $result = @{ Result = $false FileName = $null FileNames = @() SafeFileName = $null SafeFileNames = @() SelectedPath = $null } $dialogResult = $this.Dialog.ShowDialog($owner) if ($dialogResult -eq 'OK') { $result.Result = $true if ($this.DialogType -eq "Folder") { $result.SelectedPath = $this.Dialog.SelectedPath $result.FileName = $this.Dialog.SelectedPath } else { $result.FileName = $this.Dialog.FileName $result.SafeFileName = $this.Dialog.SafeFileName if ($this.DialogType -eq "Open") { $result.FileNames = $this.Dialog.FileNames $result.SafeFileNames = $this.Dialog.SafeFileNames } else { $result.FileNames = @($this.Dialog.FileName) $result.SafeFileNames = @($this.Dialog.SafeFileName) } } } return $result } # Gibt das Dialog-Objekt zurück (für erweiterte Szenarien) [object] Build() { return $this.Dialog } # Dispose des Dialogs [void] Dispose() { if ($null -ne $this.Dialog) { $this.Dialog.Dispose() } } } class TemplateBuilder : BaseComponent{ [System.Collections.Specialized.OrderedDictionary] $Template # Konstruktor: Pfad zu PSD1-Datei TemplateBuilder([System.String]$TemplatePath) { $this.Template = $this.ConvertFromPowerShellDataString($TemplatePath) } # Konstruktor: Hashtable direkt übergeben TemplateBuilder([hashtable]$TemplateHashtable) { $this.Template = $this.ConvertHashtableToOrdered($TemplateHashtable) } # Konstruktor: OrderedDictionary direkt übergeben TemplateBuilder([System.Collections.Specialized.OrderedDictionary]$TemplateOrdered) { $this.Template = $TemplateOrdered } # Hilfsmethode: Konvertiert eine normale Hashtable in OrderedDictionary (rekursiv) hidden [System.Collections.Specialized.OrderedDictionary] ConvertHashtableToOrdered([hashtable]$Hashtable) { $ordered = New-Object System.Collections.Specialized.OrderedDictionary foreach ($key in $Hashtable.Keys) { $value = $Hashtable[$key] # Rekursive Konvertierung für verschachtelte Hashtables if ($value -is [hashtable]) { $ordered[$key] = $this.ConvertHashtableToOrdered($value) } # Array mit Hashtables elseif ($value -is [array]) { $arrayResult = @() foreach ($item in $value) { if ($item -is [hashtable]) { $arrayResult += $this.ConvertHashtableToOrdered($item) } else { $arrayResult += $item } } $ordered[$key] = $arrayResult } else { $ordered[$key] = $value } } return $ordered } # Concat mit einer dynamischen Anzahl von Argumenten static [string] Concat([object[]]$input) { return (($input | ForEach-Object { [string]$_ }) -join '') } # ToLower-Methode static [string] ToLower([object[]]$input) { if ($input.Count -lt 1) { return "" } return ([string]$input[0]).ToLowerInvariant() } # ToUpper-Methode static [string] ToUpper([object[]]$input) { if ($input.Count -lt 1) { return "" } return ([string]$input[0]).ToUpperInvariant() } # FirstIndexOf-Methode static [string] FirstIndexOf([object[]] $input){ if ($input.Count -lt 1) { return "" } return [string]$input[0] } # LastIndexOf-Methode static [string] LastIndexOf([object[]] $input){ if ($input.Count -lt 1) { return "" } return [string]$input[-1] } # IndexOf-Methode static [string] IndexOf([object[]] $input){ if ($input.Count -lt 2) { throw "IndexOf benötigt mindestens zwei Argumente." } return [string]$input[[int]$input[-1]] } # Substring-Methode static [String] Substring([object[]] $input){ if($input.Count -eq 2){ return ([string]$input[0]).Substring([int]$input[1]) }elseif($input.Count -eq 3){ return ([string]$input[0]).Substring([int]$input[1],[int]$input[2]) }else{ throw "Substring erwartet zwei oder drei Argumente." } } # Replace-Methode static [String] Replace([object[]] $input){ if ($input.Count -lt 3) { throw "Replace erwartet drei Argumente." } return ([string]$input[0]).Replace([string]$input[1],[string]$input[2]) } hidden [System.Collections.Specialized.OrderedDictionary] ResolveDeploymentParameterDefaultValues(){ $TemplateClone = $this.Template if($TemplateClone.Contains("Parameters")){ foreach($Parameter in $this.Template.Parameters.GetEnumerator()){ if(!$Parameter.Value.Contains("Value")){ $TemplateClone.Parameters[$Parameter.Name].Value = $this.Template.Parameters[$Parameter.Name].DefaultValue } } } return $TemplateClone } [System.Collections.Specialized.OrderedDictionary] ResolveDeploymentDataVariables(){ $TemplateClone = $this.ResolveDeploymentParameterDefaultValues() # Erst Variables auflösen (können Parameter-Referenzen enthalten) if ($TemplateClone.Variables) { $resolvedVariables = New-Object System.Collections.Specialized.OrderedDictionary # WICHTIG: Keys-Array erstellen, um Collection-Modification zu vermeiden $varKeys = @($TemplateClone.Variables.Keys) foreach ($varKey in $varKeys) { $resolvedVariables[$varKey] = $this.ResolveValue($TemplateClone.Variables[$varKey],$TemplateClone.Parameters,$TemplateClone.Variables) } $TemplateClone.Variables = $resolvedVariables } # Dann den Rest des Templates auflösen # WICHTIG: Keys-Array erstellen, um Collection-Modification zu vermeiden $templateKeys = @($TemplateClone.Keys) foreach ($key in $templateKeys) { if ($key -ne 'Variables' -and $key -ne 'Parameters') { $TemplateClone[$key] = $this.ResolveValue($TemplateClone[$key],$TemplateClone.Parameters,$TemplateClone.Variables) } } return $TemplateClone } hidden [System.Collections.Specialized.OrderedDictionary] ConvertFromPowerShellDataString($TemplatePath){ $errors = $null $tokens = $null $ast = [System.Management.Automation.Language.Parser]::ParseFile( $TemplatePath, [ref]$tokens, [ref]$errors ) if ($errors) { throw "Fehler beim Parsen der Datei: $($errors[0].Message)" } $hashtableAst = $ast.Find({ $args[0] -is [System.Management.Automation.Language.HashtableAst] }, $true) if (-not $hashtableAst) { throw "Keine Hashtable im $TemplatePath gefunden" } $this.Template = $this.ConvertToOrderedHashtable($hashtableAst) return $this.Template } hidden [System.Collections.Specialized.OrderedDictionary] ConvertToOrderedHashtable([System.Management.Automation.Language.HashtableAst]$HashtableAst){ $ordered = New-Object System.Collections.Specialized.OrderedDictionary $sortedKvps = $HashtableAst.KeyValuePairs | Sort-Object { $_.Item1.Extent.StartOffset } foreach ($kvp in $sortedKvps) { $key = [string]$this.ConvertAstValue($kvp.Item1) $value = $this.ConvertAstValue($kvp.Item2) $ordered.Add($key, $value) } return $ordered } hidden [System.Object] ConvertAstValue([System.Management.Automation.Language.Ast]$Ast) { if ($null -eq $Ast) { return $null } if ($Ast -is [System.Management.Automation.Language.PipelineAst]) { if ($Ast.PipelineElements.Count -eq 1 -and $Ast.PipelineElements[0] -is [System.Management.Automation.Language.CommandExpressionAst]) { return $this.ConvertAstValue($Ast.PipelineElements[0].Expression) } return $Ast.SafeGetValue() } if ($Ast -is [System.Management.Automation.Language.CommandExpressionAst]) { return $this.ConvertAstValue($Ast.Expression) } if ($Ast -is [System.Management.Automation.Language.ParenExpressionAst]) { return $this.ConvertAstValue($Ast.Pipeline) } if ($Ast -is [System.Management.Automation.Language.ArrayExpressionAst]) { return $this.ConvertAstValue($Ast.SubExpression) } if ($Ast -is [System.Management.Automation.Language.StatementBlockAst]) { if ($Ast.Statements.Count -eq 1 -and $Ast.Statements[0] -is [System.Management.Automation.Language.PipelineAst]) { return $this.ConvertAstValue($Ast.Statements[0]) } throw "Nicht unterstützter Ausdruck in PSD1-Datei: $($Ast.Extent.Text)" } if ($Ast -is [System.Management.Automation.Language.ArrayLiteralAst]) { $result = @() foreach ($element in $Ast.Elements) { $result += $this.ConvertAstValue($element) } return $result } if ($Ast -is [System.Management.Automation.Language.HashtableAst]) { return $this.ConvertToOrderedHashtable($Ast) } if ($Ast -is [System.Management.Automation.Language.VariableExpressionAst]) { switch ($Ast.VariablePath.UserPath) { 'true' { return $true } 'false' { return $false } 'null' { return $null } default { return $Ast.Extent.Text } } } try { return $Ast.SafeGetValue() } catch { return $Ast.Extent.Text } } hidden [System.Object] ResolveValue($Value, $Parameters, $Variables){ $resolutionStack = [System.Collections.Generic.List[string]]::new() return $this.ResolveValueInternal($Value, $Parameters, $Variables, $resolutionStack) } hidden [System.Object] ResolveValueInternal($Value, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack){ if ($Value -is [string]) { $trimmedValue = $Value.Trim() if ($this.IsTemplateExpression($trimmedValue)) { return $this.EvaluateTemplateExpression($trimmedValue.Substring(1, $trimmedValue.Length - 2), $Parameters, $Variables, $ResolutionStack) } return $this.ResolveInlineTemplateExpressions($Value, $Parameters, $Variables, $ResolutionStack) } elseif ($Value -is [System.Collections.IDictionary]) { $resolved = New-Object System.Collections.Specialized.OrderedDictionary foreach ($key in $Value.Keys) { $resolved[$key] = $this.ResolveValueInternal($Value[$key], $Parameters, $Variables, $ResolutionStack) } return $resolved } elseif ($Value -is [array]) { $resolved = @() foreach ($item in $Value) { $resolved += $this.ResolveValueInternal($item, $Parameters, $Variables, $ResolutionStack) } return $resolved } else { return $Value } } hidden [bool] IsTemplateExpression([string]$Value) { if ([string]::IsNullOrWhiteSpace($Value)) { return $false } if (-not ($Value.StartsWith('[') -and $Value.EndsWith(']'))) { return $false } return $this.IsFunctionInvocation($Value.Substring(1, $Value.Length - 2)) } hidden [bool] IsFunctionInvocation([string]$Value) { return $Value.Trim() -match '^[A-Za-z_][A-Za-z0-9_]*\s*\(.*\)$' } hidden [string] ResolveInlineTemplateExpressions([string]$Value, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) { $resolved = $Value $matches = [regex]::Matches($Value, '\[[^\[\]]+\]') for ($i = $matches.Count - 1; $i -ge 0; $i--) { $match = $matches[$i] $expressionText = $match.Value.Substring(1, $match.Value.Length - 2) if (-not $this.IsFunctionInvocation($expressionText)) { continue } $replacement = $this.EvaluateTemplateExpression($expressionText, $Parameters, $Variables, $ResolutionStack) $resolved = $resolved.Remove($match.Index, $match.Length).Insert($match.Index, [string]$replacement) } return $resolved } hidden [System.Object] EvaluateTemplateExpression([string]$ExpressionText, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) { $trimmedExpression = $ExpressionText.Trim() if ($trimmedExpression -notmatch '^([A-Za-z_][A-Za-z0-9_]*)\s*\((.*)\)$') { return $ExpressionText } $functionName = $matches[1] $argumentsText = $matches[2] $resolvedArguments = @() $result = $null foreach ($argument in $this.SplitExpressionArguments($argumentsText)) { $resolvedArguments += ,($this.ResolveExpressionToken($argument, $Parameters, $Variables, $ResolutionStack)) } switch ($functionName.ToLowerInvariant()) { 'parameter' { $result = $this.GetParameterValue([string]$resolvedArguments[0], $Parameters); break } 'parameters' { $result = $this.GetParameterValue([string]$resolvedArguments[0], $Parameters); break } 'variable' { $result = $this.GetVariableValue([string]$resolvedArguments[0], $Parameters, $Variables, $ResolutionStack); break } 'variables' { $result = $this.GetVariableValue([string]$resolvedArguments[0], $Parameters, $Variables, $ResolutionStack); break } 'concat' { $result = [TemplateBuilder]::Concat($resolvedArguments); break } 'tolower' { $result = [TemplateBuilder]::ToLower($resolvedArguments); break } 'toupper' { $result = [TemplateBuilder]::ToUpper($resolvedArguments); break } 'firstindexof' { $result = [TemplateBuilder]::FirstIndexOf($resolvedArguments); break } 'lastindexof' { $result = [TemplateBuilder]::LastIndexOf($resolvedArguments); break } 'indexof' { $result = [TemplateBuilder]::IndexOf($resolvedArguments); break } 'substring' { $result = [TemplateBuilder]::Substring($resolvedArguments); break } 'replace' { $result = [TemplateBuilder]::Replace($resolvedArguments); break } default { throw "Nicht unterstützte Template-Funktion '$functionName'" return $null } } return $result } hidden [string[]] SplitExpressionArguments([string]$ArgumentsText) { $arguments = New-Object 'System.Collections.Generic.List[string]' if ([string]::IsNullOrWhiteSpace($ArgumentsText)) { return $arguments.ToArray() } $builder = New-Object System.Text.StringBuilder $depth = 0 $inSingleQuote = $false $inDoubleQuote = $false for ($i = 0; $i -lt $ArgumentsText.Length; $i++) { $char = $ArgumentsText[$i] if ($char -eq "'" -and -not $inDoubleQuote) { [void]$builder.Append($char) if ($inSingleQuote -and $i + 1 -lt $ArgumentsText.Length -and $ArgumentsText[$i + 1] -eq "'") { $i++ [void]$builder.Append($ArgumentsText[$i]) continue } $inSingleQuote = -not $inSingleQuote continue } if ($char -eq '"' -and -not $inSingleQuote) { $inDoubleQuote = -not $inDoubleQuote [void]$builder.Append($char) continue } if (-not $inSingleQuote -and -not $inDoubleQuote) { if ($char -eq '(') { $depth++ } elseif ($char -eq ')') { $depth-- } elseif ($char -eq ',' -and $depth -eq 0) { $arguments.Add($builder.ToString().Trim()) [void]$builder.Clear() continue } } [void]$builder.Append($char) } if ($builder.Length -gt 0 -or $ArgumentsText.Trim().Length -gt 0) { $arguments.Add($builder.ToString().Trim()) } return $arguments.ToArray() } hidden [System.Object] ResolveExpressionToken([string]$Token, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) { $trimmedToken = $Token.Trim() if ([string]::IsNullOrWhiteSpace($trimmedToken)) { return '' } if ($this.IsTemplateExpression($trimmedToken)) { return $this.EvaluateTemplateExpression($trimmedToken.Substring(1, $trimmedToken.Length - 2), $Parameters, $Variables, $ResolutionStack) } if ($this.IsFunctionInvocation($trimmedToken)) { return $this.EvaluateTemplateExpression($trimmedToken, $Parameters, $Variables, $ResolutionStack) } if (($trimmedToken.StartsWith("'") -and $trimmedToken.EndsWith("'")) -or ($trimmedToken.StartsWith('"') -and $trimmedToken.EndsWith('"'))) { return $this.UnquoteString($trimmedToken) } if ($trimmedToken -match '^\$?true$') { return $true } if ($trimmedToken -match '^\$?false$') { return $false } if ($trimmedToken -match '^\$?null$') { return $null } if ($trimmedToken -match '^-?\d+$') { return [int]$trimmedToken } if ($trimmedToken -match '^-?\d+\.\d+$') { return [double]$trimmedToken } return $trimmedToken } hidden [string] UnquoteString([string]$Value) { if ($Value.StartsWith("'") -and $Value.EndsWith("'")) { return ($Value.Substring(1, $Value.Length - 2) -replace "''", "'") } if ($Value.StartsWith('"') -and $Value.EndsWith('"')) { return $Value.Substring(1, $Value.Length - 2) } return $Value } hidden [System.Object] GetParameterValue([string]$ParameterName, $Parameters) { if ($null -eq $Parameters -or -not $Parameters.Contains($ParameterName)) { throw "Parameter '$ParameterName' nicht gefunden" } $parameter = $Parameters[$ParameterName] if ($parameter -is [System.Collections.IDictionary]) { if ($parameter.Contains('Value') -and $null -ne $parameter.Value) { return $parameter.Value } if ($parameter.Contains('DefaultValue')) { return $parameter.DefaultValue } } return $parameter } hidden [System.Object] GetVariableValue([string]$VariableName, $Parameters, $Variables, [System.Collections.Generic.List[string]]$ResolutionStack) { if ($null -eq $Variables -or -not $Variables.Contains($VariableName)) { throw "Variable '$VariableName' nicht gefunden" return $null } if ($ResolutionStack.Contains($VariableName)) { $cycle = @($ResolutionStack.ToArray()) + $VariableName throw "Zirkuläre Variablenreferenz erkannt: $($cycle -join ' -> ')" return $null } $ResolutionStack.Add($VariableName) $resolvedValue = $null try { $resolvedValue = $this.ResolveValueInternal($Variables[$VariableName], $Parameters, $Variables, $ResolutionStack) } finally { [void]$ResolutionStack.Remove($VariableName) } return $resolvedValue } } class TabControlBuilder : BaseComponent{ [System.Windows.Forms.TabControl] $control TabControlBuilder() { $this.control = New-Object System.Windows.Forms.TabControl } [TabControlBuilder] SetDock([System.String]$dock) { $this.control.Dock = [System.Windows.Forms.DockStyle]::$dock return $this } [TabControlBuilder] SetWidth([Int]$width) { $this.control.Width = $width return $this } [TabControlBuilder] SetVisible([Boolean]$visible) { $this.control.Visible = $visible return $this } [TabControlBuilder] SetSelectedIndex([int]$index) { $this.control.SelectedIndex = $index return $this } [TabControlBuilder] AddTabPage([System.Windows.Forms.TabPage]$tabPage) { $this.control.TabPages.Add($tabPage) | Out-Null return $this } [System.Windows.Forms.TabControl] Build() { return $this.control } } class TabPageBuilder : BaseComponent{ [System.Windows.Forms.TabPage]$control TabPageBuilder([System.String]$text) { $this.control = New-Object System.Windows.Forms.TabPage $this.control.Text = $text } [TabPageBuilder] SetPadding([int]$all) { $this.control.Padding = New-Object System.Windows.Forms.Padding($all) return $this } [TabPageBuilder] AddControl([System.Windows.Forms.Control]$childControl) { $this.control.Controls.Add($childControl) return $this } [void] AddTo([System.Windows.Forms.Control]$parent) { $parent.Controls.Add($this.control) } [System.Windows.Forms.TabPage] Build() { return $this.control } } class ListViewBuilder : BaseComponent { [System.Windows.Forms.ListView]$control ListViewBuilder() { $this.control = New-Object System.Windows.Forms.ListView $this.control.View = 'Details' } [ListViewBuilder] SetDock([System.String]$dock) { $this.control.Dock = [System.Windows.Forms.DockStyle]::$dock return $this } [ListViewBuilder] SetView([System.String]$view) { $this.control.View = [System.Windows.Forms.View]::$view return $this } [ListViewBuilder] SetFullRowSelect([bool]$select) { $this.control.FullRowSelect = $select return $this } [ListViewBuilder] SetGridLines([bool]$gridLines) { $this.control.GridLines = $gridLines return $this } [ListViewBuilder] AddColumn([System.String]$text, [int]$width) { $this.control.Columns.Add($text, $width) | Out-Null return $this } [ListViewBuilder] AddDoubleClickHandler([scriptblock]$handler) { $this.control.Add_DoubleClick($handler) return $this } [System.Windows.Forms.ListView] Build() { return $this.control } } #endregion Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $settingsManager = [SettingsManager]::new("DSC Tool") # Verwendung $logManager = [LogManager]::new("DSC Tool") $logManager.Info("Anwendung gestartet") [BaseComponent]::InitializeShared($settingsManager, $logManager) foreach($function in $(Get-ChildItem -Path $settingsManager.Get("FunctionsPath") -Recurse -Filter "*.ps1" -File)){ $logManager.Info("Lade Funktion: $($function.FullName)") try{ . $function.FullName }catch{ $logManager.Error("Fehler beim Laden: $_") } } $stateManager = [StateManager]::new() $stateManager.SetStates(@{ hasSelection = $false isMerged = $false isResolved = $false }) # Menü erstellen $menuBuilder = ( [MenuBuilder]::new(). BuildFromStructure($settingsManager.GetMenuStructure()). WithStateManager($stateManager) ) $menuHandlers = @{ "Import" = @{ Handler = { Import-ConfigurationData $statusBar.SetText("Status", "Deployment importiert.") } Condition = { -not $stateManager.GetState("isMerged") } } "Export" = @{ Handler = { Export-ConfigurationData $statusBar.SetText("Status", "Deployment exportiert.") } Condition = { $stateManager.GetState("isMerged") -eq $true } } "Refresh" = @{ Handler = { $statusBar.SetText("Status", "Wird aktualisiert...") } } "Exit" = { $form.Form.Close() } "OpenFolder" = @{ Handler = { $result = [DialogBuilder]::SelectFolder(). SetDescription("Wählen Sie den Root-Ordner für Templates"). SetSelectedPath($settingsManager.Get("RootPath", "C:\")). SetShowNewFolderButton($true). SetUseDescriptionForTitle($true). Show() if ($result.Result) { $settingsManager.Set("RootPath", $result.SelectedPath) $statusBar.SetText("Status", "Ordner gesetzt: $($result.SelectedPath)") #Load-Templates } } } "ResetMenu" = { $result = [System.Windows.Forms.MessageBox]::Show( "Möchten Sie das Menü auf die Standardeinstellungen zurücksetzen?`n`nDie Anwendung wird danach neu gestartet.", "Menü zurücksetzen", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Question ) if ($result -eq 'Yes') { $settingsManager.ResetMenuStructure() [System.Windows.Forms.MessageBox]::Show( "Menü wurde zurückgesetzt. Bitte starten Sie die Anwendung neu.", "Erfolg", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information ) $form.Form.Close() } } "About" = { [System.Windows.Forms.MessageBox]::Show( "DSC Tool v1.0`n`nEin modernes Tool zur Verwaltung von DSC-Konfigurationen.`n`n© 2024", "Über DSC Tool", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information ) } } # Handler setzen $menuBuilder.SetClickHandlersWithConditions($menuHandlers) # StatusBar erstellen $statusBar = [StatusStripBuilder]::new() # FlowLayoutPanel erstellen $flow = ( [FlowLayoutPanelBuilder]::new(). SetPadding(6). Build() ) ## Rechte Seite # Rechtes Label $tabControl = ( [TabControlBuilder]::new(). SetDock("Fill"). Build() ) $buttonClear = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xed62). SetToolTip("Clear"). Build() ) $tabControlButtonPanel = ( [FlowLayoutPanelBuilder]::new(). SetDock("Right"). SetPadding(6). SetWidth(52). SetFlowDirection("TopDown"). SetWrapContents($false). AddControls(@($buttonClear)). Build() ) $tabPanel = ( [PanelBuilder]::new(). SetDock("Top"). SetHeight(200). BindEnabled({ $stateManager.GetState("isResolved") -eq $true }, $stateManager). AddControls(@($tabControl,$tabControlButtonPanel)). Build() ) $rightLabel = ( [LabelBuilder]::new("Datei-Inhalt / Details"). SetDock('Top'). SetHeight(24). SetFont("Segoe UI", 9, [System.Drawing.FontStyle]::Bold). SetTextAlign([System.Drawing.ContentAlignment]::MiddleLeft). SetPadding(5, 0, 0, 0). Build() ) # TreeView $treeView = ( [TreeViewBuilder]::new(). SetFont("Consolas", 10). AddNode("Keine Auswahl"). Build() ) $buttonImport = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xed25). SetToolTip("Import - Datei laden"). BindEnabled({ -not $stateManager.GetState("isMerged") }, $stateManager). SetClickHandler({ Import-ConfigurationData }). Build() ) $buttonMerge = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xe9f5). SetToolTip("Merge - Ausgewählte Dateien zusammenführen"). BindEnabled({ $selectionList.Items.Count -gt 0 -and $stateManager.GetState("isMerged") -eq $false }, $stateManager). SetClickHandler({ param($s, $e) if($selectionList.Items.Count -gt 0){ Merge-Templates $stateManager.SetState("isResolved",$true) }else{ [System.Windows.Forms.MessageBox]::Show("Keine Einträge vorhanden","Info",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information) } }). Build() ) $buttonClear = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xed62). SetToolTip("Clear - TreeView leeren"). BindEnabled({ $treeView.Nodes.Count -gt 0 }, $stateManager). SetClickHandler({ $treeView.Nodes.Clear() $treeView.Nodes.Add("Keine Auswahl") | Out-Null $statusBar.SetText("Status", "TreeView geleert") $stateManager.SetStates(@{ "isMerged" = $false "isResolved" = $false }) }). Build() ) $buttonExport = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xe74e). SetToolTip("Export - Als PSD1 speichern"). BindEnabled({ $stateManager.GetState("isResolved") -eq $true }, $stateManager). SetClickHandler({ Export-ConfigurationData }). Build() ) $treeButtonPanel = ( [FlowLayoutPanelBuilder]::new(). SetDock("Right"). SetPadding(6). SetWidth(52). SetFlowDirection("TopDown"). SetWrapContents($false). AddControls(@($buttonImport, $buttonMerge, $buttonClear, $buttonExport)). Build() ) $treeViewPanel = ( [PanelBuilder]::new(). SetDock("Fill"). AddControls(@($treeView,$treeButtonPanel)). Build() ) $selectionLabel = ( [LabelBuilder]::new("Ausgewählte Dateien"). SetHeight(20). SetDock("Bottom"). Build() ) $selectionList = ( [ListBoxBuilder]::new(). SetFont("Segoe UI", 9). SetDock('Fill'). # Füllt die GroupBox aus SetHeight(120). Build() ) $buttonMoveUp = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xe74a). SetToolTip("Element nach oben verschieben"). BindEnabled({ $selectionList.Items.Count -gt 1 }, $stateManager). SetClickHandler({ $SelectedIndex = $selectionList.SelectedIndex if ($SelectedIndex -gt 0) { $item = $selectionList.Items[$SelectedIndex] $selectionList.Items.RemoveAt($SelectedIndex) $selectionList.Items.Insert($SelectedIndex-1, $item) $selectionList.SelectedIndex = $SelectedIndex-1 $stateManager.SetState("isMerged",$false) } }). Build() ) $buttonRemoveSelection = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xe894). SetToolTip("Element löschen"). BindEnabled({ $selectionList.Items.Count -gt 0 }, $stateManager). SetClickHandler({ $SelectedIndex = $selectionList.SelectedIndex if ($SelectedIndex -ge 0) { $removed = $selectionList.Items[$SelectedIndex] $resp = [System.Windows.Forms.MessageBox]::Show("Eintrag wirklich löschen?`r`n$($removed.FullName)","Bestätigen",[System.Windows.Forms.MessageBoxButtons]::YesNo,[System.Windows.Forms.MessageBoxIcon]::Question) if ($resp -eq [System.Windows.Forms.DialogResult]::Yes) { $selectionList.Items.RemoveAt($SelectedIndex) $stateManager.SetState("hasSelection", $selectionList.Items.Count -gt 0) $stateManager.UpdateAllButtons() $statusBar.SetText("Status","Eintrag entfernt: $($removed.FullName)") if ($selectionList.Items.Count -eq 0) { Update-TreeView } else { Merge-Templates } } } else { [System.Windows.Forms.MessageBox]::Show("Kein Eintrag ausgewählt","Info",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information) } }). Build() ) $buttonClearSelection = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xe74d). SetToolTip("Element löschen"). BindEnabled({ $selectionList.Items.Count -gt 0 }, $stateManager). SetClickHandler({ if ($SelectedIndex -ge 0) { $selectionList.Items.Clear() } else { [System.Windows.Forms.MessageBox]::Show("Kein Eintrag vorhanden","Info",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information) } Update-TreeView }). Build() ) $buttonMoveDown = ( [ButtonBuilder]::new(). SetFluentIcon([char]0xe74b). SetToolTip("Element nach unten verschieben"). BindEnabled({ $selectionList.Items.Count -gt 1 }, $stateManager). SetClickHandler({ $SelectedIndex = $selectionList.SelectedIndex if ($SelectedIndex -ge 0 -and $SelectedIndex -lt ($selectionList.Items.Count - 1)) { $item = $selectionList.Items[$SelectedIndex] $selectionList.Items.RemoveAt($SelectedIndex) $selectionList.Items.Insert($SelectedIndex+1, $item) $selectionList.SelectedIndex = $SelectedIndex+1 $stateManager.SetState("isMerged",$false) } }). Build() ) $selectionButtonPanel = ( [FlowLayoutPanelBuilder]::new(). SetDock("Right"). SetPadding(6). SetWidth(48). SetFlowDirection("TopDown"). SetWrapContents($false). AddControls(@($buttonMoveUp,$buttonRemoveSelection,$buttonClearSelection,$buttonMoveDown)). Build() ) $selectionBottomPanel = ( [PanelBuilder]::new(). SetDock('Bottom'). SetHeight(200). AddControls(@($selectionList,$selectionButtonPanel)). Build() ) # SplitPanel erstellen $splitPanel = ( [SplitPanelBuilder]::new('Vertical', 0.30, 100, 150). AddToPanel1($flow). ClearPanel2(). AddToPanel2($treeViewPanel). AddToPanel2($rightLabel). AddToPanel2($selectionLabel). AddToPanel2($selectionBottomPanel). AddToPanel2($tabPanel) ) # Form erstellen $form = ( [FormBuilder]::new("Test"). SetSize( $settingsManager.Get("WindowWidth", 1024), $settingsManager.Get("WindowHeight", 768) ). AddMenu($menuBuilder). AddStatusBar($statusBar). AddSplitPanel($splitPanel). AddKeyDownHandler({ param($s, $e) # Ctrl+S für Export if ($e.Control -and $e.KeyCode -eq 'S') { if ($stateManager.GetState("isMerged")) { Export-ConfigurationData } $e.Handled = $true } # Ctrl+O für Import if ($e.Control -and $e.KeyCode -eq 'O') { $buttonImport.PerformClick() $e.Handled = $true } # F5 für Refresh if ($e.KeyCode -eq 'F5') { Load-Templates $e.Handled = $true } # Ctrl+M für Merge if ($e.Control -and $e.KeyCode -eq 'M') { if ($selectionList.Items.Count -gt 0) { Merge-Templates } $e.Handled = $true } }). AddLoadHandler({ # Beim Laden der Form $rootPath = $settingsManager.Get("RootPath") if ($rootPath -and (Test-Path $rootPath)) { $statusBar.AddLabel("Status", "Lade gespeicherten Pfad: $rootPath") } Load-Templates # Registriere Layout-Container für automatisches Resize [BaseComponent]::AccessibilityManager.RegisterLayoutContainer($flow) # Registriere alle Controls für Accessibility $form.RegisterAllAccessibleControls() | Out-Null }). AddFormClosedHandler({ # Settings speichern $settingsManager.Set("WindowWidth", $form.Form.Width) $settingsManager.Set("WindowHeight", $form.Form.Height) $settingsManager.Set("SplitterDistance", $splitPanel.SplitContainer.SplitterDistance) # Ressourcen freigeben $treeView.Dispose() $selectionList.Dispose() $flow.Dispose() }). AddShownHandler({ # ZUERST: SplitPanel initialisieren $splitPanel.InitializeAfterShown($form.Form) # DANN: Padding setzen $splitPanel.SetPanelPaddingWithMenu(1, $menuBuilder.GetMenuStrip(), 6, 6, 6) $splitPanel.SetPanelPaddingWithMenu(2, $menuBuilder.GetMenuStrip(), 6, 6, $($statusBar.GetStatusStrip().Height + 6)) foreach($Groupbox in $flow.Controls){ # GroupBox-Breite anpassen $Groupbox.Width = $flow.ClientSize.Width - 20 } $splitPanel.AutoSizeSplitterToPanel1Content() # Focus setzen $treeView.Focus() # StatusBar Update $statusBar.SetText("Status", "Initialisierung abgeschlossen") }) ) # Initial alle Button-States aktualisieren $stateManager.UpdateAllButtons() $form.Show()