|
# For usage instructions scroll down |
|
|
|
function Show-SelectizeDialog { |
|
[CmdletBinding()] |
|
param( |
|
[Parameter(Mandatory)] |
|
[AllowEmptyCollection()] |
|
[object[]] $Items, |
|
|
|
[string] $DisplayProperty, |
|
|
|
[string] $ValueProperty, |
|
|
|
[string] $Title = 'Select items', |
|
|
|
[string] $Prompt = 'Search and select one or more items...', |
|
|
|
[bool] $DarkMode = $false, |
|
|
|
[ValidateRange(420, 4000)] |
|
[int] $Width = 560, |
|
|
|
[ValidateRange(360, 4000)] |
|
[int] $Height = 520 |
|
) |
|
|
|
if ([Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') { |
|
throw @' |
|
WPF requires an STA thread. |
|
|
|
Run PowerShell using one of these commands: |
|
|
|
powershell.exe -STA |
|
pwsh.exe -STA |
|
'@ |
|
} |
|
|
|
Add-Type -AssemblyName PresentationFramework |
|
Add-Type -AssemblyName PresentationCore |
|
Add-Type -AssemblyName WindowsBase |
|
|
|
# Convert the supplied values into consistent internal objects. |
|
$index = 0 |
|
|
|
$wrappedItems = @( |
|
foreach ($item in $Items) { |
|
if ($DisplayProperty) { |
|
$property = $item.PSObject.Properties[$DisplayProperty] |
|
|
|
if ($null -eq $property) { |
|
throw "An item does not contain display property '$DisplayProperty'." |
|
} |
|
|
|
$display = [string] $property.Value |
|
} |
|
else { |
|
$display = [string] $item |
|
} |
|
|
|
if ($ValueProperty) { |
|
$property = $item.PSObject.Properties[$ValueProperty] |
|
|
|
if ($null -eq $property) { |
|
throw "An item does not contain value property '$ValueProperty'." |
|
} |
|
|
|
$value = $property.Value |
|
} |
|
else { |
|
$value = $item |
|
} |
|
|
|
[pscustomobject] @{ |
|
Index = $index |
|
Display = $display |
|
Value = $value |
|
Original = $item |
|
} |
|
|
|
$index++ |
|
} |
|
) |
|
|
|
# Centralized light and dark color palettes. |
|
$theme = if ($DarkMode) { |
|
[pscustomobject] @{ |
|
WindowBackground = '#202020' |
|
Surface = '#2B2B2B' |
|
SurfaceAlternate = '#323232' |
|
Border = '#4A4A4A' |
|
BorderStrong = '#707070' |
|
Text = '#F5F5F5' |
|
MutedText = '#B5B5B5' |
|
Accent = '#60CDFF' |
|
AccentText = '#000000' |
|
ChipBackground = '#334A5E' |
|
ChipBorder = '#5BA6D6' |
|
ChipText = '#F5F5F5' |
|
Hover = '#3A3A3A' |
|
Selection = '#3D566A' |
|
SelectionText = '#FFFFFF' |
|
ButtonBackground = '#323232' |
|
ButtonBorder = '#5A5A5A' |
|
} |
|
} |
|
else { |
|
[pscustomobject] @{ |
|
WindowBackground = '#F5F5F5' |
|
Surface = '#FFFFFF' |
|
SurfaceAlternate = '#F8F8F8' |
|
Border = '#D0D0D0' |
|
BorderStrong = '#A0A0A0' |
|
Text = '#1F1F1F' |
|
MutedText = '#707070' |
|
Accent = '#0067C0' |
|
AccentText = '#FFFFFF' |
|
ChipBackground = '#E4F0FA' |
|
ChipBorder = '#8AB8DF' |
|
ChipText = '#1F1F1F' |
|
Hover = '#EEEEEE' |
|
Selection = '#D6E9F8' |
|
SelectionText = '#1F1F1F' |
|
ButtonBackground = '#FFFFFF' |
|
ButtonBorder = '#C7C7C7' |
|
} |
|
} |
|
|
|
$escapedTitle = [Security.SecurityElement]::Escape($Title) |
|
$escapedPrompt = [Security.SecurityElement]::Escape($Prompt) |
|
|
|
[xml] $xaml = @" |
|
<Window |
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|
Title="$escapedTitle" |
|
Width="$Width" |
|
Height="$Height" |
|
MinWidth="420" |
|
MinHeight="360" |
|
WindowStartupLocation="CenterScreen" |
|
ResizeMode="CanResize" |
|
Background="$($theme.WindowBackground)" |
|
Foreground="$($theme.Text)" |
|
FontFamily="Segoe UI Variable Text"> |
|
|
|
<Window.Resources> |
|
<Style TargetType="Button"> |
|
<Setter Property="Padding" Value="16,7"/> |
|
<Setter Property="Margin" Value="6,0,0,0"/> |
|
<Setter Property="MinWidth" Value="85"/> |
|
<Setter Property="Cursor" Value="Hand"/> |
|
<Setter Property="Background" Value="$($theme.ButtonBackground)"/> |
|
<Setter Property="Foreground" Value="$($theme.Text)"/> |
|
<Setter Property="BorderBrush" Value="$($theme.ButtonBorder)"/> |
|
</Style> |
|
|
|
<Style TargetType="ListBoxItem"> |
|
<Setter Property="Padding" Value="10,8"/> |
|
<Setter Property="HorizontalContentAlignment" Value="Stretch"/> |
|
<Setter Property="Foreground" Value="$($theme.Text)"/> |
|
<Setter Property="Background" Value="Transparent"/> |
|
|
|
<Style.Triggers> |
|
<Trigger Property="IsMouseOver" Value="True"> |
|
<Setter Property="Background" Value="$($theme.Hover)"/> |
|
</Trigger> |
|
|
|
<Trigger Property="IsSelected" Value="True"> |
|
<Setter Property="Background" Value="$($theme.Selection)"/> |
|
<Setter Property="Foreground" Value="$($theme.SelectionText)"/> |
|
</Trigger> |
|
</Style.Triggers> |
|
</Style> |
|
</Window.Resources> |
|
|
|
<Grid Margin="18"> |
|
<Grid.RowDefinitions> |
|
<RowDefinition Height="Auto"/> |
|
<RowDefinition Height="*"/> |
|
<RowDefinition Height="Auto"/> |
|
</Grid.RowDefinitions> |
|
|
|
<!-- Combined selection accumulator and filter --> |
|
<Border |
|
x:Name="InputBorder" |
|
Grid.Row="0" |
|
Margin="0,0,0,10" |
|
MinHeight="48" |
|
MaxHeight="130" |
|
Background="$($theme.Surface)" |
|
BorderBrush="$($theme.BorderStrong)" |
|
BorderThickness="1" |
|
CornerRadius="7"> |
|
|
|
<ScrollViewer |
|
HorizontalScrollBarVisibility="Disabled" |
|
VerticalScrollBarVisibility="Auto"> |
|
|
|
<WrapPanel |
|
x:Name="InputPanel" |
|
Margin="7"> |
|
|
|
<Grid |
|
x:Name="SearchContainer" |
|
MinWidth="210" |
|
Margin="2"> |
|
|
|
<TextBox |
|
x:Name="SearchBox" |
|
MinWidth="200" |
|
Padding="5,6" |
|
BorderThickness="0" |
|
Background="Transparent" |
|
Foreground="$($theme.Text)" |
|
CaretBrush="$($theme.Text)" |
|
FontSize="14"/> |
|
|
|
<TextBlock |
|
x:Name="SearchPlaceholder" |
|
Margin="6,0,0,0" |
|
VerticalAlignment="Center" |
|
Foreground="$($theme.MutedText)" |
|
IsHitTestVisible="False" |
|
Text="$escapedPrompt"/> |
|
</Grid> |
|
</WrapPanel> |
|
</ScrollViewer> |
|
</Border> |
|
|
|
<!-- Filtered choices --> |
|
<Border |
|
Grid.Row="1" |
|
Background="$($theme.Surface)" |
|
BorderBrush="$($theme.Border)" |
|
BorderThickness="1" |
|
CornerRadius="7"> |
|
|
|
<ListBox |
|
x:Name="ChoicesList" |
|
BorderThickness="0" |
|
Background="$($theme.Surface)" |
|
Foreground="$($theme.Text)" |
|
DisplayMemberPath="Display" |
|
SelectionMode="Single" |
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled"/> |
|
</Border> |
|
|
|
<!-- Dialog buttons --> |
|
<StackPanel |
|
Grid.Row="2" |
|
Margin="0,14,0,0" |
|
HorizontalAlignment="Right" |
|
Orientation="Horizontal"> |
|
|
|
<Button |
|
x:Name="CancelButton" |
|
Content="Cancel" |
|
IsCancel="True"/> |
|
|
|
<Button |
|
x:Name="OkButton" |
|
Content="OK" |
|
IsDefault="True" |
|
Background="$($theme.Accent)" |
|
Foreground="$($theme.AccentText)" |
|
BorderBrush="$($theme.Accent)"/> |
|
</StackPanel> |
|
</Grid> |
|
</Window> |
|
"@ |
|
|
|
$reader = [System.Xml.XmlNodeReader]::new($xaml) |
|
|
|
try { |
|
$window = [Windows.Markup.XamlReader]::Load($reader) |
|
} |
|
finally { |
|
$reader.Close() |
|
} |
|
|
|
$inputBorder = $window.FindName('InputBorder') |
|
$inputPanel = $window.FindName('InputPanel') |
|
$searchContainer = $window.FindName('SearchContainer') |
|
$searchBox = $window.FindName('SearchBox') |
|
$searchPlaceholder = $window.FindName('SearchPlaceholder') |
|
$choicesList = $window.FindName('ChoicesList') |
|
$okButton = $window.FindName('OkButton') |
|
$cancelButton = $window.FindName('CancelButton') |
|
|
|
$state = [pscustomobject] @{ |
|
Window = $window |
|
InputBorder = $inputBorder |
|
InputPanel = $inputPanel |
|
SearchContainer = $searchContainer |
|
SearchBox = $searchBox |
|
SearchPlaceholder = $searchPlaceholder |
|
ChoicesList = $choicesList |
|
Items = $wrappedItems |
|
SelectedItems = [System.Collections.Generic.List[object]]::new() |
|
Theme = $theme |
|
Accepted = $false |
|
|
|
RefreshChoices = $null |
|
RefreshInput = $null |
|
AddCurrentItem = $null |
|
RemoveItem = $null |
|
} |
|
|
|
$refreshChoices = { |
|
param( |
|
[Parameter(Mandatory)] |
|
[object] $State |
|
) |
|
|
|
$query = $State.SearchBox.Text.Trim() |
|
|
|
$selectedIndexes = @( |
|
$State.SelectedItems | |
|
ForEach-Object { $_.Index } |
|
) |
|
|
|
$filteredItems = @( |
|
$State.Items | |
|
Where-Object { |
|
$notSelected = $_.Index -notin $selectedIndexes |
|
|
|
$matchesFilter = |
|
[string]::IsNullOrWhiteSpace($query) -or |
|
$_.Display.IndexOf( |
|
$query, |
|
[StringComparison]::CurrentCultureIgnoreCase |
|
) -ge 0 |
|
|
|
$notSelected -and $matchesFilter |
|
} |
|
) |
|
|
|
$State.ChoicesList.ItemsSource = $null |
|
$State.ChoicesList.ItemsSource = $filteredItems |
|
|
|
if ($filteredItems.Count -gt 0) { |
|
$State.ChoicesList.SelectedIndex = 0 |
|
} |
|
else { |
|
$State.ChoicesList.SelectedIndex = -1 |
|
} |
|
} |
|
|
|
$removeItem = { |
|
param( |
|
[Parameter(Mandatory)] |
|
[object] $State, |
|
|
|
[Parameter(Mandatory)] |
|
[object] $Entry |
|
) |
|
|
|
[void] $State.SelectedItems.Remove($Entry) |
|
|
|
& $State.RefreshInput $State |
|
& $State.RefreshChoices $State |
|
|
|
[void] $State.SearchBox.Focus() |
|
} |
|
|
|
$refreshInput = { |
|
param( |
|
[Parameter(Mandatory)] |
|
[object] $State |
|
) |
|
|
|
$searchHadFocus = $State.SearchBox.IsKeyboardFocused |
|
|
|
$State.InputPanel.Children.Clear() |
|
|
|
foreach ($entry in $State.SelectedItems) { |
|
$chip = [Windows.Controls.Border]::new() |
|
|
|
$chip.Background = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$State.Theme.ChipBackground |
|
) |
|
) |
|
|
|
$chip.BorderBrush = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$State.Theme.ChipBorder |
|
) |
|
) |
|
|
|
$chip.BorderThickness = [Windows.Thickness]::new(1) |
|
$chip.CornerRadius = [Windows.CornerRadius]::new(12) |
|
$chip.Padding = [Windows.Thickness]::new(9, 3, 4, 3) |
|
$chip.Margin = [Windows.Thickness]::new(0, 0, 6, 5) |
|
$chip.Cursor = 'Arrow' |
|
$chip.Focusable = $true |
|
$chip.ToolTip = 'Click to focus; press Backspace or Delete to remove' |
|
|
|
$chipContent = [Windows.Controls.StackPanel]::new() |
|
$chipContent.Orientation = 'Horizontal' |
|
|
|
$chipText = [Windows.Controls.TextBlock]::new() |
|
$chipText.Text = $entry.Display |
|
$chipText.VerticalAlignment = 'Center' |
|
$chipText.Margin = [Windows.Thickness]::new(1, 0, 2, 0) |
|
|
|
$chipText.Foreground = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$State.Theme.ChipText |
|
) |
|
) |
|
|
|
$removeButton = [Windows.Controls.Button]::new() |
|
$removeButton.Content = [char] 0x00D7 |
|
$removeButton.ToolTip = "Remove $($entry.Display)" |
|
$removeButton.MinWidth = 21 |
|
$removeButton.MinHeight = 20 |
|
$removeButton.Padding = [Windows.Thickness]::new(2, 0, 2, 0) |
|
$removeButton.Margin = [Windows.Thickness]::new(5, 0, 0, 0) |
|
$removeButton.BorderThickness = [Windows.Thickness]::new(0) |
|
$removeButton.Background = [Windows.Media.Brushes]::Transparent |
|
$removeButton.Cursor = 'Hand' |
|
$removeButton.Focusable = $false |
|
|
|
$removeButton.Foreground = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$State.Theme.ChipText |
|
) |
|
) |
|
|
|
$chipContext = [pscustomobject] @{ |
|
State = $State |
|
Entry = $entry |
|
} |
|
|
|
$chip.Tag = $chipContext |
|
$removeButton.Tag = $chipContext |
|
|
|
$chip.Add_MouseLeftButtonDown({ |
|
param($sender, $eventArgs) |
|
|
|
# Do not consume clicks intended for the remove button. |
|
$element = $eventArgs.OriginalSource |
|
|
|
while ($null -ne $element -and $element -ne $sender) { |
|
if ($element -is [Windows.Controls.Button]) { |
|
return |
|
} |
|
|
|
try { |
|
$element = [Windows.Media.VisualTreeHelper]::GetParent( |
|
$element |
|
) |
|
} |
|
catch { |
|
$element = $null |
|
} |
|
} |
|
|
|
[void] $sender.Focus() |
|
$eventArgs.Handled = $true |
|
}) |
|
|
|
$chip.Add_GotKeyboardFocus({ |
|
param($sender, $eventArgs) |
|
|
|
$context = $sender.Tag |
|
|
|
if ($null -eq $context) { |
|
return |
|
} |
|
|
|
$sender.BorderBrush = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$context.State.Theme.Accent |
|
) |
|
) |
|
|
|
$sender.BorderThickness = [Windows.Thickness]::new(2) |
|
}) |
|
|
|
$chip.Add_LostKeyboardFocus({ |
|
param($sender, $eventArgs) |
|
|
|
$context = $sender.Tag |
|
|
|
if ($null -eq $context) { |
|
return |
|
} |
|
|
|
$sender.BorderBrush = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$context.State.Theme.ChipBorder |
|
) |
|
) |
|
|
|
$sender.BorderThickness = [Windows.Thickness]::new(1) |
|
}) |
|
|
|
$chip.Add_PreviewKeyDown({ |
|
param($sender, $eventArgs) |
|
|
|
if ( |
|
$eventArgs.Key -ne 'Back' -and |
|
$eventArgs.Key -ne 'Delete' |
|
) { |
|
return |
|
} |
|
|
|
$context = $sender.Tag |
|
|
|
if ( |
|
$null -eq $context -or |
|
$null -eq $context.State -or |
|
$null -eq $context.Entry |
|
) { |
|
return |
|
} |
|
|
|
$eventArgs.Handled = $true |
|
|
|
& $context.State.RemoveItem ` |
|
$context.State ` |
|
$context.Entry |
|
}) |
|
|
|
$removeButton.Add_Click({ |
|
param($sender, $eventArgs) |
|
|
|
$context = $sender.Tag |
|
|
|
if ( |
|
$null -eq $context -or |
|
$null -eq $context.State -or |
|
$null -eq $context.Entry |
|
) { |
|
return |
|
} |
|
|
|
& $context.State.RemoveItem ` |
|
$context.State ` |
|
$context.Entry |
|
}) |
|
|
|
[void] $chipContent.Children.Add($chipText) |
|
[void] $chipContent.Children.Add($removeButton) |
|
|
|
$chip.Child = $chipContent |
|
|
|
[void] $State.InputPanel.Children.Add($chip) |
|
} |
|
|
|
[void] $State.InputPanel.Children.Add($State.SearchContainer) |
|
|
|
$State.SearchPlaceholder.Visibility = if ( |
|
$State.SearchBox.Text.Length -gt 0 -or |
|
$State.SelectedItems.Count -gt 0 |
|
) { |
|
'Collapsed' |
|
} |
|
else { |
|
'Visible' |
|
} |
|
|
|
if ($searchHadFocus) { |
|
[void] $State.SearchBox.Focus() |
|
} |
|
} |
|
|
|
$addCurrentItem = { |
|
param( |
|
[Parameter(Mandatory)] |
|
[object] $State |
|
) |
|
|
|
$entry = $State.ChoicesList.SelectedItem |
|
|
|
if ($null -eq $entry) { |
|
return |
|
} |
|
|
|
[void] $State.SelectedItems.Add($entry) |
|
|
|
$State.SearchBox.Clear() |
|
|
|
& $State.RefreshInput $State |
|
& $State.RefreshChoices $State |
|
|
|
[void] $State.SearchBox.Focus() |
|
} |
|
|
|
$state.RefreshChoices = $refreshChoices |
|
$state.RefreshInput = $refreshInput |
|
$state.AddCurrentItem = $addCurrentItem |
|
$state.RemoveItem = $removeItem |
|
|
|
$window.Tag = $state |
|
$inputBorder.Tag = $state |
|
$searchBox.Tag = $state |
|
$choicesList.Tag = $state |
|
$okButton.Tag = $state |
|
$cancelButton.Tag = $state |
|
|
|
$inputBorder.Add_MouseLeftButtonDown({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -ne $dialogState) { |
|
[void] $dialogState.SearchBox.Focus() |
|
} |
|
}) |
|
|
|
$searchBox.Add_GotKeyboardFocus({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -eq $dialogState) { |
|
return |
|
} |
|
|
|
$dialogState.InputBorder.BorderBrush = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$dialogState.Theme.Accent |
|
) |
|
) |
|
|
|
$dialogState.InputBorder.BorderThickness = ( |
|
[Windows.Thickness]::new(2) |
|
) |
|
}) |
|
|
|
$searchBox.Add_LostKeyboardFocus({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -eq $dialogState) { |
|
return |
|
} |
|
|
|
$dialogState.InputBorder.BorderBrush = ( |
|
[Windows.Media.BrushConverter]::new().ConvertFromString( |
|
$dialogState.Theme.BorderStrong |
|
) |
|
) |
|
|
|
$dialogState.InputBorder.BorderThickness = ( |
|
[Windows.Thickness]::new(1) |
|
) |
|
}) |
|
|
|
$searchBox.Add_TextChanged({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -eq $dialogState) { |
|
return |
|
} |
|
|
|
$dialogState.SearchPlaceholder.Visibility = if ( |
|
$sender.Text.Length -gt 0 -or |
|
$dialogState.SelectedItems.Count -gt 0 |
|
) { |
|
'Collapsed' |
|
} |
|
else { |
|
'Visible' |
|
} |
|
|
|
& $dialogState.RefreshChoices $dialogState |
|
}) |
|
|
|
$searchBox.Add_PreviewKeyDown({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -eq $dialogState) { |
|
return |
|
} |
|
|
|
switch ($eventArgs.Key) { |
|
'Enter' { |
|
& $dialogState.AddCurrentItem $dialogState |
|
$eventArgs.Handled = $true |
|
} |
|
|
|
'Down' { |
|
if ($dialogState.ChoicesList.Items.Count -gt 0) { |
|
[void] $dialogState.ChoicesList.Focus() |
|
$dialogState.ChoicesList.SelectedIndex = 0 |
|
} |
|
|
|
$eventArgs.Handled = $true |
|
} |
|
|
|
'Back' { |
|
if ( |
|
$sender.Text.Length -eq 0 -and |
|
$dialogState.SelectedItems.Count -gt 0 |
|
) { |
|
$lastEntry = $dialogState.SelectedItems[ |
|
$dialogState.SelectedItems.Count - 1 |
|
] |
|
|
|
& $dialogState.RemoveItem ` |
|
$dialogState ` |
|
$lastEntry |
|
|
|
$eventArgs.Handled = $true |
|
} |
|
} |
|
|
|
'Delete' { |
|
if ( |
|
$sender.Text.Length -eq 0 -and |
|
$dialogState.SelectedItems.Count -gt 0 |
|
) { |
|
$lastEntry = $dialogState.SelectedItems[ |
|
$dialogState.SelectedItems.Count - 1 |
|
] |
|
|
|
& $dialogState.RemoveItem ` |
|
$dialogState ` |
|
$lastEntry |
|
|
|
$eventArgs.Handled = $true |
|
} |
|
} |
|
|
|
'Left' { |
|
if ( |
|
$sender.Text.Length -eq 0 -and |
|
$dialogState.InputPanel.Children.Count -gt 1 |
|
) { |
|
$lastChipIndex = |
|
$dialogState.InputPanel.Children.Count - 2 |
|
|
|
$lastChip = |
|
$dialogState.InputPanel.Children[$lastChipIndex] |
|
|
|
if ($lastChip.Focusable) { |
|
[void] $lastChip.Focus() |
|
$eventArgs.Handled = $true |
|
} |
|
} |
|
} |
|
|
|
'Escape' { |
|
$dialogState.Window.Close() |
|
$eventArgs.Handled = $true |
|
} |
|
} |
|
}) |
|
|
|
$choicesList.Add_MouseDoubleClick({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -ne $dialogState) { |
|
& $dialogState.AddCurrentItem $dialogState |
|
} |
|
}) |
|
|
|
$choicesList.Add_PreviewKeyDown({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -eq $dialogState) { |
|
return |
|
} |
|
|
|
switch ($eventArgs.Key) { |
|
'Enter' { |
|
& $dialogState.AddCurrentItem $dialogState |
|
$eventArgs.Handled = $true |
|
} |
|
|
|
'Space' { |
|
& $dialogState.AddCurrentItem $dialogState |
|
$eventArgs.Handled = $true |
|
} |
|
|
|
'Escape' { |
|
[void] $dialogState.SearchBox.Focus() |
|
$eventArgs.Handled = $true |
|
} |
|
} |
|
}) |
|
|
|
$okButton.Add_Click({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -eq $dialogState) { |
|
return |
|
} |
|
|
|
$dialogState.Accepted = $true |
|
$dialogState.Window.DialogResult = $true |
|
}) |
|
|
|
$cancelButton.Add_Click({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -ne $dialogState) { |
|
$dialogState.Window.DialogResult = $false |
|
} |
|
}) |
|
|
|
$window.Add_ContentRendered({ |
|
param($sender, $eventArgs) |
|
|
|
$dialogState = $sender.Tag |
|
|
|
if ($null -eq $dialogState) { |
|
return |
|
} |
|
|
|
& $dialogState.RefreshInput $dialogState |
|
& $dialogState.RefreshChoices $dialogState |
|
|
|
[void] $dialogState.SearchBox.Focus() |
|
}) |
|
|
|
[void] $window.ShowDialog() |
|
|
|
if ($state.Accepted) { |
|
$result = @( |
|
$state.SelectedItems | |
|
ForEach-Object { $_.Value } |
|
) |
|
|
|
Write-Output -NoEnumerate $result |
|
} |
|
else { |
|
Write-Output -NoEnumerate @() |
|
} |
|
} |