1221 lines
45 KiB
PowerShell
1221 lines
45 KiB
PowerShell
|
<#
|
|||
|
|
|||
|
Program: PoSH GUI Template
|
|||
|
Modified Date: 2023-06-07
|
|||
|
Author: Jeremy Crabtree <jcrabtree at nct911 org> / <jeremylc at gmail>
|
|||
|
Purpose: Use this template to create new GUI tools.
|
|||
|
Copyright 2023 NCT 9-1-1
|
|||
|
|
|||
|
#>
|
|||
|
|
|||
|
#CREATE HASHTABLE AND RUNSPACE FOR GUI
|
|||
|
$WPFGui = [hashtable]::Synchronized(@{ })
|
|||
|
$newRunspace = [runspacefactory]::CreateRunspace()
|
|||
|
$newRunspace.ApartmentState = "STA"
|
|||
|
$newRunspace.ThreadOptions = "UseNewThread"
|
|||
|
$newRunspace.Open()
|
|||
|
$newRunspace.SessionStateProxy.SetVariable("WPFGui", $WPFGui)
|
|||
|
|
|||
|
#Create master runspace andadd code
|
|||
|
$psCmd = [System.Management.Automation.PowerShell]::Create().AddScript( {
|
|||
|
|
|||
|
# Add WPF and Windows Forms assemblies. This must be done inside the runspace that contains the primary program code.
|
|||
|
try {
|
|||
|
Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase, System.Drawing, system.windows.forms, System.Windows.Controls.Ribbon, System.DirectoryServices.AccountManagement
|
|||
|
}
|
|||
|
catch {
|
|||
|
Throw 'Failed to load Windows Presentation Framework assemblies.'
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
Add-Type -Name Win32Util -Namespace System -MemberDefinition @'
|
|||
|
[DllImport("Kernel32.dll")]
|
|||
|
public static extern IntPtr GetConsoleWindow();
|
|||
|
|
|||
|
[DllImport("User32.dll")]
|
|||
|
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
|
|||
|
|
|||
|
[DllImport("user32.dll", SetLastError = true)]
|
|||
|
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
|||
|
|
|||
|
[DllImport("user32.dll")]
|
|||
|
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
|||
|
|
|||
|
[DllImport("user32.dll")]
|
|||
|
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
|||
|
|
|||
|
[DllImport("user32.dll")]
|
|||
|
public static extern bool BringWindowToTop(IntPtr hWnd);
|
|||
|
|
|||
|
[DllImport("user32.dll")]
|
|||
|
public static extern bool SwitchToThisWindow(IntPtr hWnd, bool fUnknown);
|
|||
|
|
|||
|
const UInt32 SWP_NOSIZE = 0x0001;
|
|||
|
const UInt32 SWP_NOMOVE = 0x0002;
|
|||
|
const UInt32 SWP_NOACTIVATE = 0x0010;
|
|||
|
const UInt32 SWP_SHOWWINDOW = 0x0040;
|
|||
|
|
|||
|
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
|
|||
|
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
|||
|
static readonly IntPtr HWND_TOP = new IntPtr(0);
|
|||
|
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
|||
|
|
|||
|
public static void SetBottom(IntPtr hWindow)
|
|||
|
{
|
|||
|
SetWindowPos(hWindow, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
|||
|
}
|
|||
|
|
|||
|
public static void SetTop(IntPtr hWindow)
|
|||
|
{
|
|||
|
SetWindowPos(hWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
|||
|
}
|
|||
|
'@
|
|||
|
}
|
|||
|
catch {
|
|||
|
Write-Verbose "Win32Util already defined"
|
|||
|
}
|
|||
|
|
|||
|
#region Utility Functions
|
|||
|
|
|||
|
# This is the list of functions to add to the InitialSessionState that is used for all Asynchronus Runsspaces
|
|||
|
$SessionFunctions = New-Object System.Collections.ArrayList
|
|||
|
|
|||
|
|
|||
|
function Invoke-Async {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Runs code, with variables, asynchronously
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
This function runs the given code in an asynchronous runspace.
|
|||
|
This lets you process data in the background while leaving the UI responsive to input
|
|||
|
|
|||
|
.PARAMETER Code
|
|||
|
The code to run in the runspace
|
|||
|
|
|||
|
.PARAMETER Variables
|
|||
|
A hashtable containing variable names and values to pass into the runspace
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
$AsyncParameters = @{
|
|||
|
Variables = @{
|
|||
|
Key1 = 'Value1'
|
|||
|
Key2 = $SomeOtherVariable
|
|||
|
}
|
|||
|
Code = @{
|
|||
|
Write-Host "Key1: $Key1`nKey2: $Key2"
|
|||
|
}
|
|||
|
}
|
|||
|
Invoke-Async @AsyncParameters
|
|||
|
|
|||
|
.NOTES
|
|||
|
It's more reliable to pass single values than copmlex objects due to the way PowerShell handles value/reference passing with objects
|
|||
|
|
|||
|
.INPUTS
|
|||
|
Variables, Code
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
None
|
|||
|
#>
|
|||
|
[CmdletBinding()]
|
|||
|
param (
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[ScriptBlock]
|
|||
|
$Code,
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[hashtable]
|
|||
|
$Variables
|
|||
|
)
|
|||
|
# Add the above code to a runspace and execute it.
|
|||
|
$PSinstance = [powershell]::Create() #| Out-File -Append -FilePath $LogFile
|
|||
|
$PSinstance.Runspace = [runspacefactory]::CreateRunspace($InitialSessionState)
|
|||
|
$PSinstance.Runspace.ApartmentState = "STA"
|
|||
|
$PSinstance.Runspace.ThreadOptions = "UseNewThread"
|
|||
|
$PSinstance.Runspace.Open()
|
|||
|
if ($Variables) {
|
|||
|
# Pass in the specified variables from $VariableList
|
|||
|
$Variables.keys.ForEach({
|
|||
|
$PSInstance.Runspace.SessionStateProxy.SetVariable($_, $Variables.$_)
|
|||
|
})
|
|||
|
}
|
|||
|
$PSInstance.AddScript($Code)
|
|||
|
$PSinstance.BeginInvoke()
|
|||
|
$WPFGui.Error = $PSInstance.Streams.Error
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Invoke-Async') | Out-Null
|
|||
|
Function New-WPFDialog() {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
This neat little function is based on the one from Brian Posey's Article on Powershell GUIs
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
I re-factored it a bit to return the resulting XaML Reader and controls as a single, named collection.
|
|||
|
|
|||
|
.PARAMETER XamlData
|
|||
|
XamlData - A string containing valid XaML data
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
$MyForm = New-WPFDialog -XamlData $XaMLData
|
|||
|
$MyForm.Exit.Add_Click({...})
|
|||
|
$null = $MyForm.UI.Dispatcher.InvokeAsync{$MyForm.UI.ShowDialog()}.Wait()
|
|||
|
|
|||
|
.NOTES
|
|||
|
Place additional notes here.
|
|||
|
|
|||
|
.LINK
|
|||
|
http://www.windowsnetworking.com/articles-tutorials/netgeneral/building-powershell-gui-part2.html
|
|||
|
|
|||
|
.INPUTS
|
|||
|
XamlData - A string containing valid XaML data
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
a collection of WPF GUI objects.
|
|||
|
#>
|
|||
|
|
|||
|
Param(
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'XaML Data defining a WPF <window>', Position = 1)]
|
|||
|
[string]$XamlData,
|
|||
|
[Parameter(Mandatory = $False, HelpMessage = 'XaML Data defining WPF <Window.Resources', Position = 2)]
|
|||
|
[string]$Resources
|
|||
|
#[Parameter(Mandatory = $True, HelpMessage = 'Synchroinize hashtable to hold UI elements', Position = 2)]
|
|||
|
#[ref]$WPFGui
|
|||
|
)
|
|||
|
# Create an XML Object with the XaML data in it
|
|||
|
[xml]$xmlWPF = $XamlData
|
|||
|
|
|||
|
#If a Resource Dictionary has been included, import and append it to our Window
|
|||
|
if ( -not [System.String]::IsNullOrEmpty( $Resources )) {
|
|||
|
[xml]$xmlResourceWPF = $Resources
|
|||
|
Foreach ($ChildNode in $xmlResourceWPF.ResourceDictionary.ChildNodes) {
|
|||
|
($ImportNode = $xmlWPF.ImportNode($ChildNode, $true)) | Out-Null
|
|||
|
$xmlWPF.Window.'Window.Resources'.AppendChild($ImportNode) | Out-Null
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
# Create the XAML reader using a new XML node reader, UI is the only hard-coded object name here
|
|||
|
$XaMLReader = New-Object System.Collections.Hashtable
|
|||
|
$XaMLReader.Add('UI', ([Windows.Markup.XamlReader]::Load((new-object -TypeName System.Xml.XmlNodeReader -ArgumentList $xmlWPF)))) | Out-Null
|
|||
|
|
|||
|
# Create hooks to each named object in the XAML reader
|
|||
|
$Elements = $xmlWPF.SelectNodes('//*[@Name]')
|
|||
|
ForEach ( $Element in $Elements ) {
|
|||
|
$VarName = $Element.Name
|
|||
|
$VarValue = $XaMLReader.UI.FindName($Element.Name)
|
|||
|
$XaMLReader.Add($VarName, $VarValue) | Out-Null
|
|||
|
}
|
|||
|
return $XaMLReader
|
|||
|
}
|
|||
|
$SessionFunctions.Add('New-WPFDialog') | Out-Null
|
|||
|
Function Set-Blur () {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Blurs the MainWindow
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
|
|||
|
|
|||
|
.PARAMETER On
|
|||
|
Turn blur on
|
|||
|
|
|||
|
.PARAMETER Off
|
|||
|
Turn blur off
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
Set-Blur -On
|
|||
|
|
|||
|
.NOTES
|
|||
|
|
|||
|
.INPUTS
|
|||
|
none
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
None
|
|||
|
#>
|
|||
|
|
|||
|
[CmdletBinding()]
|
|||
|
param (
|
|||
|
[Parameter(ParameterSetName = 'On')]
|
|||
|
[switch]
|
|||
|
$On,
|
|||
|
[Parameter(ParameterSetName = 'Off')]
|
|||
|
[switch]
|
|||
|
$Off
|
|||
|
)
|
|||
|
Switch ($PSCmdlet.ParameterSetName) {
|
|||
|
'On' {
|
|||
|
$WPFGui.MainGrid.Effect.Radius = 10
|
|||
|
}
|
|||
|
'Off' {
|
|||
|
$WPFGui.MainGrid.Effect.Radius = 0
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Set-Blur') | Out-Null
|
|||
|
function Copy-Object {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Copies a PSObject
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Copies an object to new memory. Sometimes you really need to duplicate an object, rather than just create a new pointer to it. This serializes an object then deserializes it to new memory as a brute force mechanism to copy it.
|
|||
|
|
|||
|
.PARAMETER InputObject
|
|||
|
The Object to be copied
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
$NewCopy = Copy-Object $OldObject
|
|||
|
|
|||
|
.NOTES
|
|||
|
|
|||
|
.INPUTS
|
|||
|
An object
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
A copy of an object
|
|||
|
#>
|
|||
|
param (
|
|||
|
$InputObject
|
|||
|
)
|
|||
|
$SerialObject = [System.Management.Automation.PSSerializer]::Serialize($InputObject)
|
|||
|
return [System.Management.Automation.PSSerializer]::Deserialize($SerialObject)
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Copy-Object') | Out-Null
|
|||
|
Function New-MessageDialog() {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Displays a Windows 11 styled MEssage Dialog
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
This is a utility function to display a baisc information, error, or simple input window in a Windows 11 style.
|
|||
|
|
|||
|
.PARAMETER DialogTitle
|
|||
|
'Dialog Title'
|
|||
|
|
|||
|
.PARAMETER H1
|
|||
|
'Major Header'
|
|||
|
|
|||
|
.PARAMETER DialogText
|
|||
|
'Message Text'
|
|||
|
|
|||
|
.PARAMETER CancelText
|
|||
|
'Cancel Text'
|
|||
|
|
|||
|
.PARAMETER ConfirmText
|
|||
|
'Confirm Text'
|
|||
|
|
|||
|
.PARAMETER Beep
|
|||
|
'Plays sound if set'
|
|||
|
|
|||
|
.PARAMETER GetInput
|
|||
|
'Shows input TextBox if set'
|
|||
|
|
|||
|
.PARAMETER IsError
|
|||
|
'Shows error icon if set'
|
|||
|
|
|||
|
.PARAMETER IsAsync
|
|||
|
'Process asynchronously when set'
|
|||
|
|
|||
|
.PARAMETER Owner'
|
|||
|
'Owner Window, required when this is a child'
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
$NewDialog = @{
|
|||
|
DialogTitle = 'Example Dialog'
|
|||
|
H1 = "This is a pop-up dialog"
|
|||
|
DialogText = "Dialog text should go here"
|
|||
|
ConfirmText = 'Continue'
|
|||
|
GetInput = $false
|
|||
|
Beep = $true
|
|||
|
IsError = $true
|
|||
|
Owner = $WPFGui.UI
|
|||
|
}
|
|||
|
$Dialog = New-MessageDialog @NewDialog
|
|||
|
|
|||
|
.NOTES
|
|||
|
|
|||
|
.INPUTS
|
|||
|
None
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
DialogResult, nd Text if requested
|
|||
|
#>
|
|||
|
|
|||
|
Param(
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'Dialog Title', Position = 1)]
|
|||
|
[string]$DialogTitle,
|
|||
|
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'Major Header', Position = 2)]
|
|||
|
[string]$H1,
|
|||
|
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'Message Text', Position = 3)]
|
|||
|
[string]$DialogText,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false, HelpMessage = 'Cancel Text', Position = 4)]
|
|||
|
[string]$CancelText = $null,
|
|||
|
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'Confirm Text', Position = 5)]
|
|||
|
[string]$ConfirmText,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false, HelpMessage = 'Plays sound if set', Position = 6)]
|
|||
|
[switch]$Beep,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false, HelpMessage = 'Shows input TextBox if set', Position = 7)]
|
|||
|
[switch]$GetInput,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false, HelpMessage = 'Shows error icon if set', Position = 8)]
|
|||
|
[switch]$IsError,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false, HelpMessage = 'Process asynchronously when set', Position = 9)]
|
|||
|
[switch]$IsAsync,
|
|||
|
|
|||
|
[Parameter(Mandatory = $true, HelpMessage = 'Owner Window, required when this is a child', Position = 10)]
|
|||
|
[PSObject]$Owner
|
|||
|
|
|||
|
|
|||
|
)
|
|||
|
|
|||
|
$file = $PSScriptRoot + ".\DialogPanel.xaml"
|
|||
|
$xamlData = Get-Content $file -Raw
|
|||
|
|
|||
|
$Dialog = New-WPFDialog -XamlData $xamlData
|
|||
|
|
|||
|
if ($Owner) {
|
|||
|
if ($IsAsync) {
|
|||
|
$Owner.Dispatcher.invoke([action] {
|
|||
|
$Dialog.UI.Owner = $Owner
|
|||
|
})
|
|||
|
}
|
|||
|
else {
|
|||
|
$Dialog.UI.Owner = $Owner
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$Dialog.MainWindow.Title = $DialogTitle
|
|||
|
$Dialog.DialogTitle.Text = $DialogTitle
|
|||
|
$Dialog.H1.Text = $H1
|
|||
|
$Dialog.DialogText.Text = $DialogText
|
|||
|
if ($CancelText) {
|
|||
|
$Dialog.CancelButton.Content = $CancelText
|
|||
|
}
|
|||
|
else {
|
|||
|
$Dialog.CancelButton.Visibility = 'hidden'
|
|||
|
}
|
|||
|
$Dialog.ConfirmButton.Content = $ConfirmText
|
|||
|
if ($IsError) {
|
|||
|
$Dialog.ErrorIcon.Visibility = 'Visible'
|
|||
|
$Dialog.DialogText.Margin = '8,8,0,0'
|
|||
|
}
|
|||
|
|
|||
|
if ($GetInput) {
|
|||
|
$Dialog.Input.Visibility = 'Visible'
|
|||
|
}
|
|||
|
|
|||
|
$Dialog.Add('Result', [System.Windows.Forms.DialogResult]::Cancel) | Out-Null
|
|||
|
|
|||
|
|
|||
|
$Dialog.ConfirmButton.add_Click( {
|
|||
|
$Dialog.Result = [System.Windows.Forms.DialogResult]::OK
|
|||
|
$Dialog.UI.Close()
|
|||
|
})
|
|||
|
$Dialog.CancelButton.Add_Click( {
|
|||
|
$Dialog.Result = [System.Windows.Forms.DialogResult]::Cancel
|
|||
|
$Dialog.UI.Close()
|
|||
|
})
|
|||
|
$Dialog.UI.add_ContentRendered( {
|
|||
|
if ($Beep) {
|
|||
|
[system.media.systemsounds]::Exclamation.play()
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
$null = $Dialog.UI.Dispatcher.InvokeAsync{ $Dialog.UI.ShowDialog() }.Wait()
|
|||
|
|
|||
|
return @{
|
|||
|
DialogResult = $Dialog.Result
|
|||
|
Text = $Dialog.Input.Text
|
|||
|
}
|
|||
|
}
|
|||
|
$SessionFunctions.Add('New-MessageDialog') | Out-Null
|
|||
|
function Write-Activity {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Write a colorized entry into the specified RichTextBox
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
This is usually used to write an entry into a RichTexBox which is being used as an Activity Log.
|
|||
|
|
|||
|
.PARAMETER Prefix
|
|||
|
The "prefix" text, usually program section or activity, to pre-pended to the row
|
|||
|
|
|||
|
.PARAMETER Text
|
|||
|
The actual text to write
|
|||
|
|
|||
|
.PARAMETER Stream
|
|||
|
The RichTextBox to write to
|
|||
|
|
|||
|
.PARAMETER IsError
|
|||
|
If set, this is an error message, write everything in RED
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
Write-Activity -Prefix 'PoSH GUI Template' -Text 'Example Activity Log Entry' -Stream 'Output'
|
|||
|
|
|||
|
.NOTES
|
|||
|
If you change the name of $WPFGui, you'll need to change it here.
|
|||
|
|
|||
|
.INPUTS
|
|||
|
Text
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
None
|
|||
|
#>
|
|||
|
param (
|
|||
|
# Prefix text, describe which part is printing output
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]
|
|||
|
$Prefix,
|
|||
|
# Text to be printed
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]
|
|||
|
$Text,
|
|||
|
# Output stream to be used
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]
|
|||
|
$Stream,
|
|||
|
|
|||
|
[switch]
|
|||
|
$IsError
|
|||
|
)
|
|||
|
$WPFGui.UI.Dispatcher.Invoke([action] {
|
|||
|
$DateStamp = Get-Date -Format "yyyy-MM-dd-HH-mm-ss"
|
|||
|
$TextRun = New-Object System.Windows.Documents.Run
|
|||
|
$TextRun.Foreground = "Red"
|
|||
|
$TextRun.Text = $DateStamp
|
|||
|
$Paragraph = New-Object System.Windows.Documents.Paragraph($TextRun)
|
|||
|
|
|||
|
$TextRun = New-Object System.Windows.Documents.Run
|
|||
|
$TextRun.Foreground = "#FF9A9A9A"
|
|||
|
$TextRun.Text = ":"
|
|||
|
$Paragraph.Inlines.Add($TextRun)
|
|||
|
# $WPFGui."$Stream".AppendText($TextRun)
|
|||
|
|
|||
|
$TextRun = New-Object System.Windows.Documents.Run
|
|||
|
$TextRun.Foreground = "#FF0078D7"
|
|||
|
$TextRun.Text = $Prefix
|
|||
|
$Paragraph.Inlines.Add($TextRun)
|
|||
|
# $WPFGui."$Stream".AppendText($TextRun)
|
|||
|
|
|||
|
$TextRun = New-Object System.Windows.Documents.Run
|
|||
|
$TextRun.Foreground = "#FF9A9A9A"
|
|||
|
$TextRun.Text = ": "
|
|||
|
$Paragraph.Inlines.Add($TextRun)
|
|||
|
# $WPFGui."$Stream".AppendText($TextRun)
|
|||
|
|
|||
|
$TextRun = New-Object System.Windows.Documents.Run
|
|||
|
if ( $IsError ) {
|
|||
|
$TextRun.Foreground = "Red"
|
|||
|
}
|
|||
|
else {
|
|||
|
$TextRun.Foreground = "Black"
|
|||
|
}
|
|||
|
|
|||
|
$TextRun.Text = $Text
|
|||
|
$Paragraph.Inlines.Add($TextRun) | Out-Null
|
|||
|
# $WPFGui."$Stream".AppendText($TextRun)
|
|||
|
$WPFGui."$Stream".Document.Blocks.Add($Paragraph) | Out-Null
|
|||
|
$WPFGui."$Stream".ScrollToEnd()
|
|||
|
})
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Write-Activity') | Out-Null
|
|||
|
function Write-StatusBar {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Write text, and a progress percentage, to the StatusBar area
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Writes Status text and progress to the StatusBar area.
|
|||
|
|
|||
|
.PARAMETER Progress
|
|||
|
Progress Bar value, from 0-100
|
|||
|
|
|||
|
.PARAMETER Text
|
|||
|
Status Text to display
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
Write-StatusBar -Progress 25 -Text "We're a quarter of the way there."
|
|||
|
|
|||
|
.NOTES
|
|||
|
If you change the name of $WPFGui, you'll need to change it here.
|
|||
|
|
|||
|
.INPUTS
|
|||
|
Text
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
None
|
|||
|
#>
|
|||
|
|
|||
|
param (
|
|||
|
# Prefix text, describe which part is printing output
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[int]
|
|||
|
$Progress,
|
|||
|
# Text to be printed
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]
|
|||
|
$Text
|
|||
|
)
|
|||
|
$WPFGui.UI.Dispatcher.invoke([action] {
|
|||
|
$WPFGui.Progress.Value = $Progress
|
|||
|
$WPFGui.StatusText.Text = $Text
|
|||
|
})
|
|||
|
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Write-StatusBar') | Out-Null
|
|||
|
function Save-Screenshot {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Save a Screenshot of the specified screen(s)
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Save a screenshot of the specified screen(s) in the specified format. Valid formats are BMP, JPG/JPEG, and PNG.
|
|||
|
|
|||
|
.PARAMETER FilePath
|
|||
|
Filename to save to
|
|||
|
|
|||
|
.PARAMETER Format
|
|||
|
An optional parameter to specify a specific format. If not set, then the format is guessed from FilePath
|
|||
|
|
|||
|
.PARAMETER ScreenNumber
|
|||
|
An int specifying which screen to save.
|
|||
|
|
|||
|
.PARAMETER AllScreens
|
|||
|
A switch to specify a capture of all screens.
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
Save-ScreenShot -FilePath "ScreenShot.jpg" -Format 'JPG' -ScreenNumber = 0
|
|||
|
|
|||
|
.NOTES
|
|||
|
If you change the name of $WPFGui, you'll need to change it here.
|
|||
|
|
|||
|
.INPUTS
|
|||
|
None
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
Screenshot
|
|||
|
#>
|
|||
|
param (
|
|||
|
[Parameter(Mandatory = $true)]
|
|||
|
[string]$FilePath,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[ValidateSet("BMP", "JPG", "JPEG", "PNG", "Unspecified")]
|
|||
|
[string]$Format = "Unspecified",
|
|||
|
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[ValidateScript({
|
|||
|
((0 -le $_) -and ( $_ -le (([System.Windows.Forms.Screen]::AllScreens).Count - 1) ))
|
|||
|
})]
|
|||
|
[int16]$ScreenNumber,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false)]
|
|||
|
[Switch]$AllScreens
|
|||
|
)
|
|||
|
|
|||
|
$ScreenList = [System.Windows.Forms.Screen]::AllScreens
|
|||
|
$Top = 0
|
|||
|
$Bottom = 0
|
|||
|
$Left = 0
|
|||
|
$Right = 0
|
|||
|
|
|||
|
if ($AllScreens) {
|
|||
|
foreach ($CurrentScreen in $ScreenList) {
|
|||
|
$Bounds = $CurrentScreen.Bounds
|
|||
|
if ($Top -gt $Bounds.Top) {
|
|||
|
$Top = $Bounds.Top
|
|||
|
}
|
|||
|
if ($Left -gt $Bounds.Left) {
|
|||
|
$Left = $Bounds.Left
|
|||
|
}
|
|||
|
if ($Bottom -lt $Bounds.Bottom) {
|
|||
|
$Bottom = $Bounds.Bottom
|
|||
|
}
|
|||
|
if ($Right -lt $Bounds.Right) {
|
|||
|
$Right = $Bounds.Right
|
|||
|
}
|
|||
|
}
|
|||
|
$Width = $Right - $Left
|
|||
|
$Height = $Bottom - $Top
|
|||
|
}
|
|||
|
else {
|
|||
|
$Left = $ScreenList[$ScreenNumber].Bounds.Left
|
|||
|
$Top = $ScreenList[$ScreenNumber].Bounds.Top
|
|||
|
$Right = $ScreenList[$ScreenNumber].Bounds.Right
|
|||
|
$Bottom = $ScreenList[$ScreenNumber].Bounds.Bottom
|
|||
|
$Width = $ScreenList[$ScreenNumber].Bounds.Width
|
|||
|
$Height = $ScreenList[$ScreenNumber].Bounds.Height
|
|||
|
}
|
|||
|
|
|||
|
$Bounds = [Drawing.Rectangle]::FromLTRB($Left, $Top, $Right, $Bottom)
|
|||
|
$Bitmap = New-Object Drawing.Bitmap $Width, $Height
|
|||
|
$Graphics = [Drawing.Graphics]::FromImage($Bitmap)
|
|||
|
$Graphics.CopyFromScreen($Bounds.Location, [Drawing.Point]::Empty, $Bounds.Size)
|
|||
|
|
|||
|
try {
|
|||
|
if ($Format -eq 'Unspecified') {
|
|||
|
$Format = $FilePath.Split('.')[-1]
|
|||
|
}
|
|||
|
}
|
|||
|
catch {
|
|||
|
Write-Error "Unable to determine filetype from $FilePath"
|
|||
|
}
|
|||
|
|
|||
|
$Bitmap.Save($FilePath, [System.Drawing.Imaging.ImageFormat]::$Format)
|
|||
|
|
|||
|
$Graphics.Dispose()
|
|||
|
$Bitmap.Dispose()
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Save-Screenshot') | Out-Null
|
|||
|
Function Get-FileName() {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Use a Win32 FileDialog to request a Filename from the user.
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Shows a Win32 FileOpenDialog or FleSaveDialog to request a Filename from the User
|
|||
|
|
|||
|
.PARAMETER Title
|
|||
|
The window Title
|
|||
|
|
|||
|
.PARAMETER Filter
|
|||
|
The FileType filter, see Microsoft's documentation on this.
|
|||
|
|
|||
|
.PARAMETER InitialDirectory
|
|||
|
The Initial Directory to display
|
|||
|
|
|||
|
.PARAMETER FileName
|
|||
|
The default FileName to select
|
|||
|
|
|||
|
.PARAMETER Save
|
|||
|
A switch to indicate that this is a SAVE dialog and not an OPEN dialog.
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
$FileNameParameters = @{
|
|||
|
Title = 'New Log File Name'
|
|||
|
Filter = 'LOG Files (*.LOG)|*.log|HTML Files (*.html)|*.html|RTF Files (*.rtf)|*.rtf'
|
|||
|
FileName = "$(Get-Date -Format 'yyyy-MM-dd-HHmmss').log"
|
|||
|
Save = $true
|
|||
|
}
|
|||
|
$FileName = Get-FileName @FileNameParameters
|
|||
|
|
|||
|
.NOTES
|
|||
|
|
|||
|
|
|||
|
.INPUTS
|
|||
|
Text
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
FileName
|
|||
|
#>
|
|||
|
Param(
|
|||
|
[Parameter()][string]$Title = 'Open File',
|
|||
|
[Parameter()][string]$Filter = 'All Files (*.*)|*.*',
|
|||
|
[Parameter()][string]$InitialDirectory = "$($env:HOMEDRIVE)$($env:HOMEPATH)",
|
|||
|
[Parameter()][string]$FileName = 'File.log',
|
|||
|
[switch]$Save
|
|||
|
)
|
|||
|
|
|||
|
$Result = $false
|
|||
|
# Setup and open an "Open File" Dialog
|
|||
|
If ( $Save ) {
|
|||
|
$FileDialog = New-Object Microsoft.Win32.SaveFileDialog
|
|||
|
$FileDialog.FileName = $FileName
|
|||
|
}
|
|||
|
else {
|
|||
|
$FileDialog = New-Object Microsoft.Win32.OpenFileDialog
|
|||
|
}
|
|||
|
$FileDialog.Title = $Title
|
|||
|
$FileDialog.filter = $Filter
|
|||
|
$FileDialog.initialDirectory = $InitialDirectory
|
|||
|
$FileDialog.AddExtension = $true
|
|||
|
$FileDialogResult = $FileDialog.ShowDialog($owner)
|
|||
|
|
|||
|
#"OK clicked" status
|
|||
|
$DialogOK = [System.Windows.Forms.DialogResult]::OK
|
|||
|
|
|||
|
if ($FileDialogResult -eq $DialogOK) {
|
|||
|
$Result = $FileDialog
|
|||
|
}
|
|||
|
return $Result.FileName
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Get-FileName') | Out-Null
|
|||
|
Function Get-FolderName() {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
(Ab)Use a Win32 FileOpenDialog to request a FolderName from the user.
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Shows a Win32 FileOpenDialog to request a FolderName from the User
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
$FolderName = Get-FolderName
|
|||
|
|
|||
|
.NOTES
|
|||
|
|
|||
|
.INPUTS
|
|||
|
None
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
FolderName
|
|||
|
#>
|
|||
|
|
|||
|
$Result = $false
|
|||
|
# Setup and open an "Open File" Dialog
|
|||
|
$FileDialog = New-Object Microsoft.Win32.OpenFileDialog
|
|||
|
$FileDialog.Filter = "Select Folder|(Select Folder)"
|
|||
|
$FileDialog.Title = "Select Folder"
|
|||
|
$FileDialog.FileName = "Select Folder"
|
|||
|
$FileDialog.CheckFileExists = $false
|
|||
|
$FileDialog.ValidateNames = $false
|
|||
|
$FileDialog.CheckPathExists = $true
|
|||
|
$FileDialogResult = $FileDialog.ShowDialog()
|
|||
|
|
|||
|
if ($FileDialogResult) {
|
|||
|
$Result = $FileDialog.FileName.Replace('Select Folder', '')
|
|||
|
}
|
|||
|
return $Result
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Get-FolderName') | Out-Null
|
|||
|
|
|||
|
function ConvertFrom-FlowDocument {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Converts a FlowDocument to HTML
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Converts a (simple) RichTextBox FlowDocument to HTML
|
|||
|
|
|||
|
.PARAMETER Document
|
|||
|
The FlowDocument to Convert
|
|||
|
|
|||
|
.PARAMETER Title
|
|||
|
The HTML page Title to use
|
|||
|
|
|||
|
.PARAMETER Save
|
|||
|
A switch to indicate that this is a SAVE dialog and not an OPEN dialog.
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
$html = ConvertFrom-FlowDocument -Document $Document -Title $Title
|
|||
|
|
|||
|
.NOTES
|
|||
|
|
|||
|
.INPUTS
|
|||
|
FlowDocument and Title
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
HTML document
|
|||
|
#>
|
|||
|
param (
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'System.Windows.Documents.FlowDocument to convert to HTML', Position = 1)]
|
|||
|
[System.Windows.Documents.FlowDocument]$Document,
|
|||
|
[Parameter(Mandatory = $false, HelpMessage = 'Document Title', Position = 2)]
|
|||
|
[string]$Title = ""
|
|||
|
)
|
|||
|
|
|||
|
$html = @"
|
|||
|
<html>
|
|||
|
<head>
|
|||
|
<title>
|
|||
|
$Title
|
|||
|
</title>
|
|||
|
</head>
|
|||
|
<body style=`"font-family: monospace;`">`n
|
|||
|
$($Document.Blocks.ForEach({
|
|||
|
$_.Inlines.Foreach({" <span style=`"color:$($_.Foreground.Color.ToString().Replace('#FF','#'))`">$($_.Text)</span>"})
|
|||
|
"<br/>`n"
|
|||
|
}))
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
"@
|
|||
|
return $html
|
|||
|
}
|
|||
|
$SessionFunctions.Add('ConvertFrom-FlowDocument') | Out-Null
|
|||
|
|
|||
|
function Save-FlowDocument {
|
|||
|
<#
|
|||
|
.SYNOPSIS
|
|||
|
Saves a RichTextBox FlowDocument in the requested format - LOG, RTF, or HTML.
|
|||
|
|
|||
|
.DESCRIPTION
|
|||
|
Saves a RichTextBox FlowDocument as Plain Text (LOG), RichText (RTF), or HTML
|
|||
|
|
|||
|
.PARAMETER Document
|
|||
|
FlowDocument to Save
|
|||
|
|
|||
|
.PARAMETER Format
|
|||
|
The File Format to use - TXT, RTF, or HTML
|
|||
|
|
|||
|
.PARAMETER Title
|
|||
|
The HTML page Title to use
|
|||
|
|
|||
|
.PARAMETER FileName
|
|||
|
The name of the file to write.
|
|||
|
|
|||
|
.EXAMPLE
|
|||
|
|
|||
|
Save-FlowDocument -Document $WPFGui.Output.Document -Format $Format -FileName $FileName -Title "Example logs $(Get-Date -Format 'yyyy-MM-dd-HH-mm-ss')"
|
|||
|
|
|||
|
.NOTES
|
|||
|
|
|||
|
.INPUTS
|
|||
|
FlowDocument and Title
|
|||
|
|
|||
|
.OUTPUTS
|
|||
|
HTML document
|
|||
|
#>
|
|||
|
param (
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'Windows FlowDocument', Position = 1)]
|
|||
|
[System.Windows.Documents.FlowDocument]$Document,
|
|||
|
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'Format to save - txt, html, or rtf', Position = 2)]
|
|||
|
[ValidateSet("TXT", "RTF", "HTML")]
|
|||
|
[string]$Format,
|
|||
|
|
|||
|
[Parameter(Mandatory = $false, HelpMessage = 'HTML Document Title', Position = 3)]
|
|||
|
[string]
|
|||
|
$Title,
|
|||
|
|
|||
|
[Parameter(Mandatory = $True, HelpMessage = 'Filename', Position = 4)]
|
|||
|
[string]
|
|||
|
$FileName
|
|||
|
)
|
|||
|
|
|||
|
# The TextRange is used for the TXT and RTF formats because it has a built-in .Save() method.
|
|||
|
$TextRange = [System.Windows.Documents.TextRange]::new($Document.ContentStart, $Document.ContentEnd)
|
|||
|
|
|||
|
# This is used for all three
|
|||
|
$FHand = [System.IO.FileStream]::new($FileName, 'OpenOrCreate')
|
|||
|
switch ($Format) {
|
|||
|
'txt' {
|
|||
|
$TextRange.Save($FHand, [System.Windows.DataFormats]::Text)
|
|||
|
}
|
|||
|
'html' {
|
|||
|
# Convert the FlowDocument to HTML, cast it to a collection of bytes and write it to FHand.
|
|||
|
$html = ConvertFrom-FlowDocument -Document $Document -Title $Title
|
|||
|
$htmlBytes = [byte[]][char[]]$html
|
|||
|
$FHand.Write($htmlBytes, 0, $htmlBytes.Count)
|
|||
|
}
|
|||
|
'rtf' {
|
|||
|
$TextRange.Save($FHand, [System.Windows.DataFormats]::Rtf)
|
|||
|
}
|
|||
|
}
|
|||
|
$FHand.Flush()
|
|||
|
$FHand.Close()
|
|||
|
}
|
|||
|
$SessionFunctions.Add('Save-FlowDocument') | Out-Null
|
|||
|
|
|||
|
# Create an Initial Session State for the ASync runspace and add all the functions in $SessionFunctions to it.
|
|||
|
$InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
|
|||
|
$SessionFunctions.ForEach({
|
|||
|
$SessionFunctionEntry = [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new($_, (Get-Content Function:\$_))
|
|||
|
$InitialSessionState.Commands.Add($SessionFunctionEntry) | Out-Null
|
|||
|
})
|
|||
|
|
|||
|
#endregion Utility Functions
|
|||
|
|
|||
|
#region - Setup default values
|
|||
|
|
|||
|
# Development Mode Toggle
|
|||
|
$DevMode = $true
|
|||
|
|
|||
|
# Failure Sentry. We watch this to know whether we need to bail out before trying to show the main window.
|
|||
|
$Failed = $false
|
|||
|
|
|||
|
if ($DevMode) {
|
|||
|
# If DevMode is set, we should read the UI definitions from these paths. This is useful when editing the XaML in Blend or Visual Studio.
|
|||
|
$XaMLWindowPath = '<full path to>\MainWindow.xaml'
|
|||
|
$XaMLResourceDictionaryPath = '<full path to>\ControlTemplates.XaML'
|
|||
|
# Load the UI definition.
|
|||
|
$WPFXaML = Get-Content -Raw -Path $XaMLWindowPath
|
|||
|
$ResourceXaML = Get-Content -Raw -Path $XaMLResourceDictionaryPath
|
|||
|
|
|||
|
# If DevMode is set, override normal pwd/cwd detection and set it explicitly.
|
|||
|
$ScriptPath = "<SomePath>"
|
|||
|
}
|
|||
|
else {
|
|||
|
if ($MyInvocation.MyCommand.CommandType -eq "ExternalScript") {
|
|||
|
$ScriptPath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
|
|||
|
}
|
|||
|
else {
|
|||
|
$ScriptPath = Split-Path -Parent -Path ([Environment]::GetCommandLineArgs()[0])
|
|||
|
if (!$ScriptPath) { $ScriptPath = "." }
|
|||
|
}
|
|||
|
|
|||
|
$mainWindow = $PSScriptRoot + ".\MainWindow.xaml"
|
|||
|
$WPFXaML = Get-Content $mainWindow -Raw
|
|||
|
|
|||
|
$mainWindow = $PSScriptRoot + ".\ControlTemplates.xaml"
|
|||
|
$ResourceXaML = Get-Content $mainWindow -Raw
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Build the GUI
|
|||
|
try {
|
|||
|
$WPFGui = [hashtable]::Synchronized( (New-WPFDialog -XamlData $WPFXamL -Resources $ResourceXaML) )
|
|||
|
$WPFGui.Add('ResourceXaML', $ResourceXaML)
|
|||
|
}
|
|||
|
catch {
|
|||
|
$failed = $true
|
|||
|
}
|
|||
|
|
|||
|
$WPFGui.Add('hWnd', $null)
|
|||
|
$WPFGui.Add('AsyncResult', [PSCustomObject]@{
|
|||
|
Success = $false
|
|||
|
Message = ""
|
|||
|
})
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Titlebar buttons
|
|||
|
$WPFGui.MinimizeButton.add_Click( {
|
|||
|
$WPFGui.UI.WindowState = 'Minimized'
|
|||
|
})
|
|||
|
|
|||
|
$WPFGui.RestoreButton.add_Click( {
|
|||
|
$WPFGui.UI.WindowState = 'Normal'
|
|||
|
$WPFGui.MaximizeButton.Visibility = 'Visible'
|
|||
|
$WPFGui.RestoreButton.Visibility = 'Collapsed'
|
|||
|
})
|
|||
|
|
|||
|
$WPFGui.MaximizeButton.add_Click( {
|
|||
|
$WPFGui.UI.WindowState = 'Maximized'
|
|||
|
$WPFGui.MaximizeButton.Visibility = 'Collapsed'
|
|||
|
$WPFGui.RestoreButton.Visibility = 'Visible'
|
|||
|
})
|
|||
|
$WPFGui.CloseButton.add_Click( {
|
|||
|
$WPFGui.UI.Close()
|
|||
|
})
|
|||
|
#endregion Titlebar buttons
|
|||
|
|
|||
|
#region Menu Buttons
|
|||
|
$WPFGui.SaveLogs.add_Click( {
|
|||
|
# When clicked, save the Activity Log to a file.
|
|||
|
$FileNameParameters = @{
|
|||
|
Title = 'New Log File Name'
|
|||
|
Filter = 'LOG Files (*.LOG)|*.log|HTML Files (*.html)|*.html|RTF Files (*.rtf)|*.rtf'
|
|||
|
FileName = "$(Get-Date -Format 'yyyy-MM-dd-HHmmss').log"
|
|||
|
Save = $true
|
|||
|
}
|
|||
|
$FileName = Get-FileName @FileNameParameters
|
|||
|
if ( $FileName ) {
|
|||
|
$Extension = ([System.IO.FileInfo]$FileName).Extension
|
|||
|
$Format = $Extension.Replace('.', '').Replace('log', 'txt')
|
|||
|
Save-FlowDocument -Document $WPFGui.Output.Document -Format $Format -FileName $FileName -Title "PoSH GUI Template logs $(Get-Date -Format 'yyyy-MM-dd-HH-mm-ss')"
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
$WPFGui.MenuAllgemein.add_Click({
|
|||
|
$WPFGUI.TabControl.SelectedIndex = 0
|
|||
|
$WPFGui.MenuButton.RaiseEvent((New-Object -TypeName System.Windows.RoutedEventArgs -ArgumentList $([System.Windows.Controls.Button]::ClickEvent)))
|
|||
|
})
|
|||
|
|
|||
|
$WPFGui.MenuBenutzer.add_Click({
|
|||
|
$WPFGUI.TabControl.SelectedIndex = 1
|
|||
|
$WPFGui.MenuButton.RaiseEvent((New-Object -TypeName System.Windows.RoutedEventArgs -ArgumentList $([System.Windows.Controls.Button]::ClickEvent)))
|
|||
|
})
|
|||
|
|
|||
|
$WPFGui.MenuComputer.add_Click({
|
|||
|
$WPFGUI.TabControl.SelectedIndex = 2
|
|||
|
$WPFGui.MenuButton.RaiseEvent((New-Object -TypeName System.Windows.RoutedEventArgs -ArgumentList $([System.Windows.Controls.Button]::ClickEvent)))
|
|||
|
})
|
|||
|
|
|||
|
$WPFGui.MenuExit.add_Click({
|
|||
|
$WPFGui.UI.Close()
|
|||
|
})
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Main Program buttons and fields
|
|||
|
|
|||
|
$WPFGui.MenuOpen.add_Completed( {
|
|||
|
# Flip the end points of the menu animation so that it will open when clicked and close when clicked again
|
|||
|
$AnimationParts = @('MenuToggle', 'BurgerFlipper', 'BlurPanel')
|
|||
|
|
|||
|
foreach ($Part in $AnimationParts) {
|
|||
|
$To = $WPFGui."$Part".To
|
|||
|
$From = $WPFGui."$Part".From
|
|||
|
$WPFGui."$Part".To = $From
|
|||
|
$WPFGui."$Part".From = $To
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
|
|||
|
$WPFGui.UI.add_ContentRendered( {
|
|||
|
# Once the window is visible, grab handle to it
|
|||
|
if ( $WPFGui.hWnd -eq $null) {
|
|||
|
$WPFGui.hWnd = (New-Object System.Windows.Interop.WindowInteropHelper($WPFGui.UI)).Handle
|
|||
|
}
|
|||
|
[System.Win32Util]::SetTop($WPFGui.hWnd)
|
|||
|
|
|||
|
# Write an example log entry
|
|||
|
Write-Activity -Prefix 'PoSH GUI Template' -Text 'Example Activity Log Entry' -Stream 'Output'
|
|||
|
})
|
|||
|
#endregion
|
|||
|
|
|||
|
|
|||
|
# This region is unique to the example UI and is meant to demonstrate how to code-behind the various controls
|
|||
|
#region Setup items for static comboxes
|
|||
|
|
|||
|
# The best way to bind data to list controls - ComboBoxes, ListBoxes, DataGrids, et. al. - is to use an ObservableCollection
|
|||
|
|
|||
|
# Create an ObservableCollection for the example DataGrid
|
|||
|
$WPFGui.Add('ExampleGridItemsList', (New-Object System.Collections.ObjectModel.ObservableCollection[PSCustomObject]) )
|
|||
|
|
|||
|
# Set the ObservableCollection as the ItemsSource for the DataGrid
|
|||
|
$WPFGui.ExampleGrid.ItemsSource = $WPFGUI.ExampleGridItemsList
|
|||
|
|
|||
|
# Some sample data for the grid, in CSV format
|
|||
|
$ExampleGridItems = @'
|
|||
|
"CheckBox","Description","Filename","ExtraInfo","RowIsValid"
|
|||
|
"True","Lorem ipsum dolor sit","amet.xslx","consectetur adipiscing elit","True"
|
|||
|
"False","sed do eiusmod tempor","incididunt.txt","ut labore et dolore magna aliqua.","True"
|
|||
|
"True","Ut enim ad minim veniam","quis.doc","nostrud exercitation ullamco","False"
|
|||
|
'@ | ConvertFrom-Csv
|
|||
|
|
|||
|
# Add each row to the the ObservableCollection. The DataGrid will displat this data automatically
|
|||
|
$ExampleGridItems.Foreach({ $WPFGui.ExampleGridItemsList.Add($_) | Out-Null })
|
|||
|
|
|||
|
|
|||
|
# The ComboBoxes work similarly to the DataGrid.
|
|||
|
|
|||
|
$WPFGui.Add('ComboBox1List', (New-Object System.Collections.ObjectModel.ObservableCollection[string]) )
|
|||
|
$WPFGui.ComboBox1.ItemsSource = $WPFGui.ComboBox1List
|
|||
|
foreach ($BoxItem in ('laboris nisi ut aliquip').Split(' ')) {
|
|||
|
$WPFGUI.ComboBox1List.Add([string]$BoxItem) | Out-Null
|
|||
|
}
|
|||
|
$WPFGui.ComboBox1.Items.Refresh()
|
|||
|
|
|||
|
$WPFGui.Add('ComboBox2List', (New-Object System.Collections.ObjectModel.ObservableCollection[string]) )
|
|||
|
$WPFGui.ComboBox2.ItemsSource = $WPFGui.ComboBox2List
|
|||
|
foreach ($BoxItem in ('ex ea commodo consequat').Split(' ')) {
|
|||
|
$WPFGUI.ComboBox2List.Add([string]$BoxItem) | Out-Null
|
|||
|
}
|
|||
|
$WPFGui.ComboBox2.Items.Refresh()
|
|||
|
|
|||
|
# Defaulted values for other input types
|
|||
|
$WPFGUI.Add('TextBox1Text', "dolor")
|
|||
|
$WPFGUI.TextBox1.Text = $WPFGUI.TextBox1Text
|
|||
|
$WPFGui.Add('pwd', $ScriptPath)
|
|||
|
|
|||
|
$WPFGui.Add('DomainList', (New-Object System.Collections.ObjectModel.ObservableCollection[string]))
|
|||
|
foreach ($Domain in @('Duis aute irure').Split(' ')) {
|
|||
|
$WPFGUI.DomainList.Add($Domain) | Out-Null
|
|||
|
}
|
|||
|
$WPFGUI.UserDomain.ItemsSource = $WPFGUI.DomainList
|
|||
|
|
|||
|
# Not used here, but included for completeness. This is a ButtonClick event that can be routed to a given button on the window.
|
|||
|
$WPFGui.Add('ButtonClick', (New-Object -TypeName System.Windows.RoutedEventArgs -ArgumentList $([System.Windows.Controls.Button]::ClickEvent)))
|
|||
|
|
|||
|
$WPFGUI.SetPath.Add_Click({
|
|||
|
|
|||
|
# Show a Folder Selection dialog when clicked.
|
|||
|
$Parameters = @{
|
|||
|
Title = 'Select the folder containing explorer.exe'
|
|||
|
}
|
|||
|
$ExplorerPath = Get-FolderName @Parameters
|
|||
|
$ExplorerFile = 'explorer.exe'
|
|||
|
if ( -not (Test-Path (Join-Path $ExplorerPath $ExplorerFile))) {
|
|||
|
$WPFGui.TextBox2.Foreground = "#FFFF0000"
|
|||
|
}
|
|||
|
else {
|
|||
|
$WPFGui.TextBox2.Foreground = "#FF000000"
|
|||
|
}
|
|||
|
$WPFGui.TextBox2.Text = $(Join-Path $ExplorerPath $ExplorerFile)
|
|||
|
})
|
|||
|
|
|||
|
$WPFGui.Execute.add_Click({
|
|||
|
# Run this code when execute is clicked. A sample dialog is shown synchronously, then the variable values are printed asynchronously.
|
|||
|
Set-Blur -On
|
|||
|
$NewDialog = @{
|
|||
|
DialogTitle = 'Example Dialog'
|
|||
|
H1 = "This is a pop-up dialog"
|
|||
|
DialogText = "Dialog text should go here"
|
|||
|
ConfirmText = 'Continue'
|
|||
|
GetInput = $false
|
|||
|
Beep = $true
|
|||
|
IsError = $false
|
|||
|
Owner = $WPFGui.UI
|
|||
|
}
|
|||
|
$Dialog = New-MessageDialog @NewDialog
|
|||
|
Set-Blur -Off
|
|||
|
$AsyncParameters = @{
|
|||
|
Variables = @{
|
|||
|
WPFGui = $WPFGui
|
|||
|
ComboBox1Value = $WPFGui.ComboBox1.SelectedValue
|
|||
|
ComboBox2Value = $WPFGui.ComboBox2.SelectedValue
|
|||
|
CheckBox1Check = $WPFGui.CheckBox1.IsChecked
|
|||
|
TextBox1Text = $WPFGui.TextBox1.Text
|
|||
|
TextBox2Text = $WPFGui.TextBox2.Text
|
|||
|
Domain = $WPFGui.UserDomain.SelectedValue
|
|||
|
Username = $WPFGui.UserName.Text
|
|||
|
SecurePassword = $WPFGui.Password.SecurePassword
|
|||
|
GridDataJSON = $WPFGui.ExampleGrid.Items | ConvertTo-Json
|
|||
|
}
|
|||
|
Code = {
|
|||
|
Write-Activity -Stream 'Output' -Prefix 'Example' -Text "ComboBox1 Value: $ComboBox1Value"
|
|||
|
Write-StatusBar -Progress 12.5 -Text "ComboBox1 Value"
|
|||
|
# Start-Sleep -Seconds 1
|
|||
|
|
|||
|
Write-Activity -Stream 'Output' -Prefix 'Example' -Text "ComboBox2 Value: $ComboBox2Value"
|
|||
|
Write-StatusBar -Progress 25 -Text "ComboBox2 Value"
|
|||
|
# Start-Sleep -Seconds 1
|
|||
|
|
|||
|
Write-Activity -Stream 'Output' -Prefix 'Example' -Text "CheckBox1 is $(if (-not $CheckBox1Check) { "not " })checked."
|
|||
|
Write-StatusBar -Progress 37.5 -Text "TextBox1 Value"
|
|||
|
# Start-Sleep -Seconds 1
|
|||
|
|
|||
|
Write-Activity -Stream 'Output' -Prefix 'Example' -Text "TextBox1 Text: $TextBox1Text"
|
|||
|
Write-StatusBar -Progress 50 -Text "TextBox1 Value"
|
|||
|
# Start-Sleep -Seconds 1
|
|||
|
|
|||
|
Write-Activity -Stream 'Output' -Prefix 'Example' -Text "TextBox2 Text: $TextBox2Text"
|
|||
|
Write-StatusBar -Progress 62.5 -Text "TextBox2 Value"
|
|||
|
# Start-Sleep -Seconds 1
|
|||
|
|
|||
|
Write-Activity -Stream 'Output' -Prefix 'Example' -Text "Credential Values: $Domain\$Username $SecurePassword"
|
|||
|
Write-StatusBar -Progress 87.5 -Text "Credential Values: $Domain\$Username $SecurePassword"
|
|||
|
# Start-Sleep -Seconds 1
|
|||
|
|
|||
|
Write-Activity -Stream 'Output' -Prefix 'Example' -Text "DataGrid Values (as JSON) $GridDataJSON"
|
|||
|
Write-StatusBar -Progress 100 -Text "DataGrid Values (as JSON)"
|
|||
|
# Start-Sleep -Seconds 1
|
|||
|
|
|||
|
Write-StatusBar -Progress 0 -Text "Ready."
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Invoke-Async @AsyncParameters
|
|||
|
})
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
if ( -not $Failed ) {
|
|||
|
# Setup async runspace items
|
|||
|
$WPFGui.Host = $host
|
|||
|
$WPFGui.Add('Runspace', [runspacefactory]::CreateRunspace($InitialSessionState))
|
|||
|
$WPFGui.Runspace.ApartmentState = "STA"
|
|||
|
$WPFGui.Runspace.ThreadOptions = "ReuseThread"
|
|||
|
$WPFGui.Runspace.Open()
|
|||
|
$WPFGui.UI.Dispatcher.InvokeAsync{ $WPFGui.UI.ShowDialog() }.Wait()
|
|||
|
$WPFGui.Runspace.Close()
|
|||
|
$WPFGui.Runspace.Dispose()
|
|||
|
}
|
|||
|
})
|
|||
|
$psCmd.Runspace = $newRunspace
|
|||
|
$psCmd.Invoke()
|