Private/Show-GridSelector.ps1

function Show-GridSelector {
    param (
        [Parameter(Mandatory)]
        [array]$Items,
        [string]$Title = "Select Item",
        [array]$Columns,
        [switch]$MultiSelect
    )

    Add-Type -AssemblyName PresentationFramework
    Add-Type -AssemblyName PresentationCore
    Add-Type -AssemblyName WindowsBase

    $script:selectorItems = [System.Collections.ArrayList]::new($Items)
    $script:filteredItems = [System.Collections.ArrayList]::new($Items)
    $script:exitRequested = $false

    # Build column XAML
    $columnXaml = ""
    foreach ($col in $Columns) {
        $columnXaml += "<GridViewColumn Header=`"$($col.Header)`" Width=`"$($col.Width)`" DisplayMemberBinding=`"{Binding $($col.Property)}`"/>`n"
    }

    $selectionMode = if ($MultiSelect) { "Extended" } else { "Single" }

    $xaml = @"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="$Title" Height="600" Width="900" WindowStartupLocation="CenterScreen" Topmost="True"
        Background="#1E1E1E">
    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="Background" Value="#2D2D30"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="BorderBrush" Value="#3F3F46"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Padding" Value="12,6"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#3E3E42"/>
                </Trigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="TextBox">
            <Setter Property="Background" Value="#2D2D30"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="BorderBrush" Value="#3F3F46"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="CaretBrush" Value="White"/>
        </Style>
    </Window.Resources>
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBox Name="SearchBox" Grid.Row="0" Margin="0,0,0,15" Padding="8" FontSize="14"/>
        <TextBlock Grid.Row="0" Margin="10,8,0,0" Foreground="#808080" IsHitTestVisible="False" Name="SearchPlaceholder" FontSize="14">Search...</TextBlock>

        <ListView Name="ItemList" Grid.Row="1" SelectionMode="$selectionMode"
                  Background="#252526" Foreground="White" BorderBrush="#3F3F46" BorderThickness="1">
            <ListView.Resources>
                <Style TargetType="GridViewColumnHeader">
                    <Setter Property="Background" Value="#2D2D30"/>
                    <Setter Property="Foreground" Value="White"/>
                    <Setter Property="BorderBrush" Value="#3F3F46"/>
                    <Setter Property="BorderThickness" Value="0,0,1,1"/>
                    <Setter Property="Padding" Value="8,6"/>
                    <Setter Property="FontWeight" Value="SemiBold"/>
                </Style>
                <Style TargetType="ListViewItem">
                    <Setter Property="Background" Value="Transparent"/>
                    <Setter Property="Foreground" Value="White"/>
                    <Setter Property="Padding" Value="8,4"/>
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="#2D2D30"/>
                        </Trigger>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="#094771"/>
                            <Setter Property="Foreground" Value="White"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    $columnXaml
                </GridView>
            </ListView.View>
        </ListView>

        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
            <Button Name="ExitBtn" Content="Exit Tool" Width="90" Margin="0,0,10,0">
                <Button.Style>
                    <Style TargetType="Button">
                        <Setter Property="Background" Value="#C42B1C"/>
                        <Setter Property="Foreground" Value="White"/>
                        <Setter Property="BorderBrush" Value="#A52A2A"/>
                        <Setter Property="BorderThickness" Value="1"/>
                        <Setter Property="Padding" Value="12,6"/>
                        <Setter Property="FontSize" Value="13"/>
                        <Setter Property="Cursor" Value="Hand"/>
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Background" Value="#D13438"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
            <Button Name="OkBtn" Content="OK" Width="90" Margin="0,0,10,0" IsDefault="True">
                <Button.Style>
                    <Style TargetType="Button">
                        <Setter Property="Background" Value="#0E639C"/>
                        <Setter Property="Foreground" Value="White"/>
                        <Setter Property="BorderBrush" Value="#0C5086"/>
                        <Setter Property="BorderThickness" Value="1"/>
                        <Setter Property="Padding" Value="12,6"/>
                        <Setter Property="FontSize" Value="13"/>
                        <Setter Property="Cursor" Value="Hand"/>
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Background" Value="#1177BB"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
            <Button Name="CancelBtn" Content="Cancel" Width="90" IsCancel="True"/>
        </StackPanel>
    </Grid>
</Window>
"@


    $reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]::new($xaml))
    $window = [System.Windows.Markup.XamlReader]::Load($reader)

    $searchBox = $window.FindName("SearchBox")
    $searchPlaceholder = $window.FindName("SearchPlaceholder")
    $listView = $window.FindName("ItemList")
    $exitBtn = $window.FindName("ExitBtn")
    $okBtn = $window.FindName("OkBtn")
    $cancelBtn = $window.FindName("CancelBtn")

    $listView.ItemsSource = $script:filteredItems

    # Allow space key in search box
    $window.Add_PreviewKeyDown({
        param($src, $e)
        if ($e.Key -eq [System.Windows.Input.Key]::Space) {
            $focused = [System.Windows.Input.Keyboard]::FocusedElement
            if ($focused -eq $searchBox) {
                $caretIndex = $searchBox.CaretIndex
                $searchBox.Text = $searchBox.Text.Insert($caretIndex, " ")
                $searchBox.CaretIndex = $caretIndex + 1
                $e.Handled = $true
            }
        }
    })

    # Search filter
    $searchBox.Add_TextChanged({
        $searchText = $searchBox.Text.ToLower()
        $searchPlaceholder.Visibility = if ($searchText) { "Collapsed" } else { "Visible" }

        $script:filteredItems.Clear()
        foreach ($item in $script:selectorItems) {
            $match = $false
            foreach ($col in $Columns) {
                $val = $item.($col.Property)
                if ($val -and $val.ToString().ToLower().Contains($searchText)) {
                    $match = $true
                    break
                }
            }
            if ($match) {
                $null = $script:filteredItems.Add($item)
            }
        }
        $listView.Items.Refresh()
    })

    $script:selectorResult = $null
    $exitBtn.Add_Click({
        $script:selectorResult = $null
        $script:exitRequested = $true
        $window.Close()
    })

    $okBtn.Add_Click({
        if ($MultiSelect) {
            $script:selectorResult = @($listView.SelectedItems)
        } else {
            $script:selectorResult = $listView.SelectedItem
        }
        $window.Close()
    })

    $cancelBtn.Add_Click({
        $script:selectorResult = $null
        $window.Close()
    })

    # Double-click to select (single mode)
    if (-not $MultiSelect) {
        $listView.Add_MouseDoubleClick({
            if ($listView.SelectedItem) {
                $script:selectorResult = $listView.SelectedItem
                $window.Close()
            }
        })
    }

    $null = $window.ShowDialog()
    return $script:selectorResult
}