public/window/Close-UiWindow.ps1
|
function Close-UiWindow { <# .SYNOPSIS Closes the window your code is running in (or the window that opened it). .DESCRIPTION Works anywhere PsUi calls code. The window goes the way it would from its own close button. -OnClosed, a -Modal child returns $false, and -ExportOnClose still hands back what the window captured. From an async action the window closes when the action ends,and a canceled action leaves it open. .PARAMETER Window Which window to close. Also taken by position (eg Close-UiWindow Main). - Current: the window the code is running in. Default. - Parent: the window that opened this child window. The child closes with it. - Main: the New-UiWindow window at the top and every child window with it. .PARAMETER Prompt Confirms first, with Yes and No buttons. No leaves the window open. -Prompt:$false turns the question off even with -Message so -Prompt:$unsaved asks only when $unsaved is true. .PARAMETER Message The prompt to ask. The default is 'Close this window?'. Using -Message implies -Prompt. .NOTES An error the action hits after the call goes with the window, the output window and all. Call Close-UiWindow last, and give the commands that can fail -ErrorAction Stop. .EXAMPLE New-UiButton -Text 'Close' -NoAsync -Action { Close-UiWindow } .EXAMPLE Register-UiHotkey -Key 'Escape' -NoAsync -Action { Close-UiWindow -Prompt } Escape asks 'Close this window?' and closes the window on Yes. .EXAMPLE New-UiWindow -Title 'Pick a server' -ExportOnClose -Content { New-UiDropdown -Label 'Server' -Variable 'server' -Items @('web01', 'web02') New-UiButton -Text 'OK' -NoOutput -Capture 'picked' -Action { $picked = $server Close-UiWindow } } Write-Host "You picked $picked" The OK action runs in the background, so the window waits for it to end and $picked still makes it back to the script. .EXAMPLE New-UiButton -Text 'Finish' -NoAsync -Action { Close-UiWindow -Window Main -Message 'All done. Close the wizard?' } A button in a child window that asks, then closes the child and the window behind it. #> [CmdletBinding()] param( [Parameter(Position = 0)] [ValidateSet('Current', 'Parent', 'Main')] [string]$Window = 'Current', [switch]$Prompt, [string]$Message = 'Close this window?' ) $session = Get-UiSession if (!$session -or !$session.Window) { Write-Warning 'Close-UiWindow: No PsUi window here to close.' return } # Window.Owner only reads on the UI thread $closeBlock = { param($fromWindow, $which, $askFirst, $question, $executor) $target = $fromWindow if ($which -eq 'Parent') { $target = $fromWindow.Owner } if ($which -eq 'Main') { while ($target.Owner) { $target = $target.Owner } } if (!$target) { Write-Warning "Close-UiWindow: '$($fromWindow.Title)' wasn't opened from another window, so it has no parent to close." return } $closeTarget = { # Close() drops the PresentationSource and leaves Visibility at Visible $source = [System.Windows.PresentationSource]::FromVisual($target) if (!$source -and $target.Visibility -eq 'Visible') { return } if ($askFirst -and !(Show-UiConfirmDialog -Title $target.Title -Message $question)) { return } # The main window's X does this too. A ReadKey dialog still waiting holds the close up otherwise. if (!$target.Owner) { [PsUi.KeyCaptureDialog]::CloseCurrentDialog() } $target.Close() }.GetNewClosure() # Not shown yet means -Content is still running, or has a dialog up. Closing it or any owner then breaks its Show, which hangs New-UiWindow on the main window $closeWhenShown = { $source = [System.Windows.PresentationSource]::FromVisual($fromWindow) if (!$source -and $fromWindow.Visibility -ne 'Visible') { $fromWindow.Add_ContentRendered($closeTarget) } else { & $closeTarget } }.GetNewClosure() # Hooked here because this copy lives in the window's runspace, and the action's is gone by OnComplete if ($executor) { $executor.add_OnComplete($closeWhenShown) } else { & $closeWhenShown } } $askFirst = $PSBoundParameters.ContainsKey('Message') if ($PSBoundParameters.ContainsKey('Prompt')) { $askFirst = $Prompt.IsPresent } $arguments = @($session.Window, $Window, $askFirst, $Message, $null) # -NoAsync actions and -Content if ($session.Window.Dispatcher.CheckAccess()) { & $closeBlock @arguments return } # The close waits for OnComplete, which fires after the -Capture values are saved $executor = $Global:AsyncExecutor if ($executor) { if (!$executor.IsRunning) { return } $arguments[4] = $executor } $invokeParams = @{ Dispatcher = $session.Window.Dispatcher ScriptBlock = $closeBlock ArgumentList = $arguments } $null = Invoke-OnUIThread @invokeParams } |