public/New-Control.ps1

using namespace System.Windows.Markup
using namespace System.Windows.Controls

# Assemblies werden über RequiredAssemblies im Modul-Manifest geladen.
# Add-Type hier ist redundant und wird daher weggelassen.

enum ControlType {
    CheckBox
    ComboBox
    DatePicker
    Headline
    PasswordBox
    RadioButton
    TextBox
}

# Interne Hilfsfunktion: Konvertiert bool-artige Werte robust in [bool].
# Unterstützt: $true/$false, "True"/"False", "1"/"0", "" / $null (→ $false).
# Warnt bei nicht interpretierbaren Werten und gibt den Default zurück.
function ConvertTo-BoolSafe {
    param(
        [object]$InputValue,
        [bool]$Default = $false
    )
    if ($null -eq $InputValue -or [string]::IsNullOrWhiteSpace("$InputValue")) {
        return $Default
    }
    if ($InputValue -is [bool]) {
        return $InputValue
    }
    # Numerische Strings ("1", "0", "-1") über int-Konvertierung abfangen,
    # da [System.Convert]::ToBoolean(string) nur "True"/"False" akzeptiert.
    $asString = [string]$InputValue
    if ($asString -match '^-?\d+$') {
        return ([int]$asString) -ne 0
    }

    try {
        return [System.Convert]::ToBoolean($asString)
    }
    catch {
        Write-Warning "Wert '$InputValue' konnte nicht als Boolean interpretiert werden. Verwende '$Default'."
        return $Default
    }
}

<#
.SYNOPSIS
Creates a new UI control based on the specified control type.

.DESCRIPTION
The `New-Control` function generates a WPF UI control dynamically based on the provided `ControlType`.
It supports creating Headline, TextBox, CheckBox, ComboBox, DatePicker, RadioButton and PasswordBox controls.
The function uses XAML to define the control layout and allows customization of properties such as name,
value, column widths, validation and value delimiter.

.PARAMETER ControlType
Specifies the type of control to create. Supported types are:
- Headline
- TextBox
- CheckBox
- ComboBox
- DatePicker
- RadioButton
- PasswordBox

.PARAMETER Name
Specifies the name or label of the control. This is used as the text for labels or headlines.

.PARAMETER Value
Specifies the value of the control. For ComboBox, this can be a delimited string of items.
For DatePicker, a date string. For CheckBox/RadioButton, a boolean or boolean-string ("True"/"False").

.PARAMETER ValueDelimiter
Specifies the delimiter used to split the `Value` parameter into individual items for ComboBox controls.
The value is treated as a literal string, not as a regular expression. The default delimiter is a comma (`,`).

.PARAMETER LabelWidth
Specifies the width of the label column in pixels. Default is 200.

.PARAMETER ValueWidth
Specifies the width of the value/input column in pixels. Default is 200.

.PARAMETER Required
Marks the control as a required field. When used with New-Window, the submit button validates
that required fields have a value before closing the dialog.
Accepts boolean values ($true/$false) or boolean-strings ("True"/"False", "1"/"0").
Empty strings and $null are treated as $false. Optimized for use with ConvertFrom-Csv.

.PARAMETER ValidationPattern
A regular expression pattern that the control's value must match. Applied during validation
in New-Window before the dialog can be submitted. Only relevant for text-based controls.

.PARAMETER GroupName
Specifies the group name for RadioButton controls. RadioButtons with the same GroupName
are mutually exclusive.

.INPUTS
Accepts input from the pipeline by property name for all parameters.

.OUTPUTS
Returns a WPF Grid control containing the specified UI element.

.EXAMPLE
# Create a headline control
$Control = New-Control -ControlType Headline -Name "My Headline"

.EXAMPLE
# Create a TextBox control with a default value
$Control = New-Control -ControlType TextBox -Name "Enter Text" -Value "Default Text"

.EXAMPLE
# Create a CheckBox control
$Control = New-Control -ControlType CheckBox -Name "Enable Feature" -Value $true

.EXAMPLE
# Create a ComboBox control with multiple items
$Control = New-Control -ControlType ComboBox -Name "Select Option" -Value "Option1,Option2,Option3"

.EXAMPLE
# Create a DatePicker control
$Control = New-Control -ControlType DatePicker -Name "Start Date"

.EXAMPLE
# Create grouped RadioButton controls
$Control1 = New-Control -ControlType RadioButton -Name "Option A" -GroupName "MyGroup" -Value $true
$Control2 = New-Control -ControlType RadioButton -Name "Option B" -GroupName "MyGroup"

.EXAMPLE
# Create a PasswordBox control
$Control = New-Control -ControlType PasswordBox -Name "Password"

.EXAMPLE
# Create a required TextBox with regex validation
$Control = New-Control -ControlType TextBox -Name "E-Mail" -Required $true -ValidationPattern '^[\w\.-]+@[\w\.-]+\.\w+$'

.EXAMPLE
# Pipeline usage with ConvertFrom-Csv (Required as string "True"/"False" is supported)
$Content = @'
ControlType;Name;Value;Required
TextBox;Vorname;;True
TextBox;E-Mail;;True
ComboBox;Abteilung;Entwicklung,Marketing,Vertrieb;
DatePicker;Startdatum;;True
'@ | ConvertFrom-Csv -Delimiter ';' | New-Control
#>

function New-Control {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ControlType]$ControlType,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$Name,

        [Parameter(ValueFromPipelineByPropertyName)]
        [object]$Value,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$ValueDelimiter = ',',

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateRange(20, 2000)]
        [int]$LabelWidth = 200,

        [Parameter(ValueFromPipelineByPropertyName)]
        [ValidateRange(20, 2000)]
        [int]$ValueWidth = 200,

        [Parameter(ValueFromPipelineByPropertyName)]
        [AllowNull()]
        [AllowEmptyString()]
        [string]$Required = '',

        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$ValidationPattern,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$GroupName
    )

    process {
        # Required: robuste Konvertierung — unterstützt $true/$false, "True"/"False", "1"/"0" und leere Strings (→ $false).
        [bool]$IsRequired = ConvertTo-BoolSafe -InputValue $Required

        $BaseControl = $null

        try {
            $BaseControl = switch ($ControlType) {
                'Headline' {
                    $Xaml = @"
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="5">
    <TextBlock x:Name="Headline" FontSize="12" FontWeight="Bold" Margin="5" />
</Grid>
"@

                    $Control = [XamlReader]::Parse($Xaml)
                    $Control.FindName('Headline').Text = $Name
                    $Control
                }

                'TextBox' {
                    $Xaml = @"
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="$LabelWidth"/>
        <ColumnDefinition Width="$ValueWidth"/>
    </Grid.ColumnDefinitions>
    <Label x:Name="LabelControl" Content="Placeholder Label Text:" Margin="0 0 10 0" HorizontalContentAlignment="Right" />
    <TextBox x:Name="ValueControl" Grid.Column="1"/>
</Grid>
"@

                    $Control = [XamlReader]::Parse($Xaml)
                    $Control.FindName('LabelControl').Content = $Name
                    $Control.FindName('ValueControl').Text = [string]$Value
                    $Control
                }

                'CheckBox' {
                    $Xaml = @"
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="$LabelWidth"/>
        <ColumnDefinition Width="$ValueWidth"/>
    </Grid.ColumnDefinitions>
    <Label x:Name="LabelControl" Margin="0 0 10 0" HorizontalContentAlignment="Right" />
    <CheckBox x:Name="ValueControl" Grid.Column="1" VerticalAlignment="Center" />
</Grid>
"@

                    $Control = [XamlReader]::Parse($Xaml)
                    $Control.FindName('LabelControl').Content = $Name
                    $Control.FindName('ValueControl').IsChecked = ConvertTo-BoolSafe -InputValue $Value
                    $Control
                }

                'ComboBox' {
                    $Xaml = @"
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="$LabelWidth"/>
        <ColumnDefinition Width="$ValueWidth"/>
    </Grid.ColumnDefinitions>
    <Label x:Name="LabelControl" Margin="0 0 10 0" HorizontalContentAlignment="Right" />
    <ComboBox x:Name="ValueControl" Grid.Column="1" />
</Grid>
"@

                    $Control = [XamlReader]::Parse($Xaml)
                    $Control.FindName('LabelControl').Content = $Name

                    $ValueControl = $Control.FindName('ValueControl')
                    $ValueControl.DisplayMemberPath = 'Name'
                    $ValueControl.SelectedValuePath = 'Value'

                    # ValueDelimiter wird als Literal-String behandelt (kein Regex).
                    $Items = [string]$Value -split [regex]::Escape($ValueDelimiter)
                    foreach ($Item in $Items) {
                        $ValueControl.Items.Add([PSCustomObject]@{ Name = $Item; Value = $Item }) | Out-Null
                    }

                    if ($ValueControl.Items.Count -gt 0) {
                        $ValueControl.SelectedIndex = 0
                    }

                    $Control
                }

                'DatePicker' {
                    $Xaml = @"
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="$LabelWidth"/>
        <ColumnDefinition Width="$ValueWidth"/>
    </Grid.ColumnDefinitions>
    <Label x:Name="LabelControl" Margin="0 0 10 0" HorizontalContentAlignment="Right" />
    <DatePicker x:Name="ValueControl" Grid.Column="1" />
</Grid>
"@

                    $Control = [XamlReader]::Parse($Xaml)
                    $Control.FindName('LabelControl').Content = $Name

                    if ($Value) {
                        try {
                            $Control.FindName('ValueControl').SelectedDate = [datetime]$Value
                        }
                        catch {
                            Write-Warning "Ungültiges Datum für '$Name': $Value"
                        }
                    }

                    $Control
                }

                'RadioButton' {
                    $Xaml = @"
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="$LabelWidth"/>
        <ColumnDefinition Width="$ValueWidth"/>
    </Grid.ColumnDefinitions>
    <Label x:Name="LabelControl" Margin="0 0 10 0" HorizontalContentAlignment="Right" />
    <RadioButton x:Name="ValueControl" Grid.Column="1" VerticalAlignment="Center" />
</Grid>
"@

                    $Control = [XamlReader]::Parse($Xaml)
                    $Control.FindName('LabelControl').Content = $Name

                    $RadioButton = $Control.FindName('ValueControl')
                    $RadioButton.IsChecked = ConvertTo-BoolSafe -InputValue $Value

                    if ($GroupName) {
                        $RadioButton.GroupName = $GroupName
                    }

                    $Control
                }

                'PasswordBox' {
                    $Xaml = @"
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="$LabelWidth"/>
        <ColumnDefinition Width="$ValueWidth"/>
    </Grid.ColumnDefinitions>
    <Label x:Name="LabelControl" Margin="0 0 10 0" HorizontalContentAlignment="Right" />
    <PasswordBox x:Name="ValueControl" Grid.Column="1" />
</Grid>
"@

                    $Control = [XamlReader]::Parse($Xaml)
                    $Control.FindName('LabelControl').Content = $Name

                    if ($Value) {
                        $Control.FindName('ValueControl').Password = [string]$Value
                    }

                    $Control
                }

                default {
                    throw "ControlType '$ControlType' wird nicht unterstützt."
                }
            }
        }
        catch {
            $PSCmdlet.ThrowTerminatingError(
                [System.Management.Automation.ErrorRecord]::new(
                    [System.InvalidOperationException]::new(
                        "Fehler beim Erstellen des Controls '$Name' (Typ: $ControlType): $($_.Exception.Message)",
                        $_.Exception
                    ),
                    'ControlCreationFailed',
                    [System.Management.Automation.ErrorCategory]::InvalidOperation,
                    $ControlType
                )
            )
        }

        # Guard: Sicherstellen dass der switch-Arm tatsächlich ein Control zurückgegeben hat.
        if ($null -eq $BaseControl) {
            $PSCmdlet.ThrowTerminatingError(
                [System.Management.Automation.ErrorRecord]::new(
                    [System.InvalidOperationException]::new(
                        "Control '$Name' (Typ: $ControlType) wurde nicht initialisiert. Interner Fehler."
                    ),
                    'ControlNotInitialized',
                    [System.Management.Automation.ErrorCategory]::InvalidResult,
                    $ControlType
                )
            )
        }

        $BaseControl.Tag = [PSCustomObject]@{
            Required          = $IsRequired
            ValidationPattern = $ValidationPattern
        }

        return $BaseControl
    }
}