How to close an open PowerShell WPF Dialog without using a button - wpf

Hi there I am launch a WPF Dialog from PowerShell with other processes controlled in a Do While loop. On exiting the loop I am hoping to close the open Dialog. Here is some of the relevant code used.
Function New-WPFDialog {
<#
.SYNOPSIS
This neat little function is based on the one from Brian Posey's Article on Powershell GUIs
.DESCRIPTION
AW re-factored 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 GUI', Position = 1)]
[string]$XamlData
)
# Add WPF and Windows Forms assemblies
try {
Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase, system.windows.forms
}
catch {
Throw 'Failed to load Windows Presentation Framework assemblies.'
}
# Create an XML Object with the XaML data in it
[xml]$xmlWPF = $XamlData
# Create the XAML reader using a new XML node reader, UI is the only hard-coded object name here
Set-Variable -Name XaMLReader -Value #{ 'UI' = ([Windows.Markup.XamlReader]::Load((new-object -TypeName System.Xml.XmlNodeReader -ArgumentList $xmlWPF))) }
# 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)
}
return $XaMLReader
}
Function New-PopUpWindowAlertFixed {
param(
[string]
$MessageText = "No Message Supplied")
# This is the XaML that defines the GUI.
$WPFXamLAlertFixed = #'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
WindowStyle="None"
Background="#006373"
Width="300"
Height="300">
<ContentControl Foreground="White">
<Grid VerticalAlignment="Center" HorizontalAlignment="Right" Background="#006373" >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Grid.Row="0">
<TextBlock Name="Message10" Margin="20,32,20,20" TextWrapping="Wrap" Text="Session Manager" FontSize="18"/>
</StackPanel>
</Grid>
</ContentControl>
</Window>
'#
$WPFGuiAlert2 = New-WPFDialog -XamlData $WPFXamlAlertFixed
$WPFGuiAlert2.Message10.Text = "Session Manager"
# $WPFGuiAlert.ApplicationExitCode = 99
<# CODE #>
$null = $WPFGUIAlert2.UI.Dispatcher.InvokeAsync{ $WPFGuiAlert2.UI.Show() }.Wait()
}
Do {
New-PopUpWindowStart
If ( $Script:VarStart -eq $true ) {
New-PopUpWindowAlertFixed
$Script:VarLoop = 1
<# I'd like to close the Dialog here #>
}
} While ( $Script:VarLoop -eq 1 )
Does anyone have any suggestions how I can close the dialog without using a button. The dialog is launched using:
$null = $WPFGUIAlert2.UI.Dispatcher.InvokeAsync{ $WPFGuiAlert2.UI.Show() }.Wait()

I resolved this by changing:
$WPFGuiAlert2 = New-WPFDialog -XamlData $WPFXamlAlertFixed
to
$Script:WPFGuiAlert2 = New-WPFDialog -XamlData $WPFXamlAlertFixed
then closing the dialog with:
$WPFGuiAlert2.UI.Close()
My error detailed null expression so realised my variable was out of scope.

Related

Get WPF button status in real time in Powershell for long running event

I am trying to change checkbox content property during its execution. If Test1 is selected and button clicked it should show immediately Test 1 Running (same applies for Test2 if selected). Once test finishes it should show Test 1 finished. Right now there are no interim changes until button task completes which ends with Test 1 finished. I was reading about WPF Toolkit BusyIndicator (cannot implement as it is closed network where I am running this) and also async-await but not sure how to implement solution powershell/xaml way.
Add-type -AssemblyName PresentationFramework, PresentationCore
$ToolboxXml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Toolbox"
WindowStartupLocation = "CenterScreen"
Height ="300"
Width ="250"
Topmost="True"
FontFamily="Arial"
WindowStyle="SingleBorderWindow">
<Grid>
<StackPanel Margin="30 10 30 0">
<StackPanel Orientation="vertical">
<CheckBox Name="chkbx1" Content="Test1"/>
<CheckBox Name="chkbx2" Content="Test2"/>
</StackPanel>
<StackPanel>
<Button Name="button1" Content="Run Tests"/>
<Label Name="label2" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
"#
$ToolboxGui = $ToolboxXml -replace "x:N",'N'
$Form = [Windows.Markup.XamlReader]::Parse(([string]$ToolboxGui))
[xml]$xaml = $ToolboxGui
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]").ForEach{
Set-Variable -Name $_.Name -Value $Form.FindName($_.Name) -Force
}
$label2.content = "Test(s) not running"
$button1.add_Click({
If ($chkbx1.IsChecked){
$Script:chkbx1.content = "Test 1 Running"
Start-Sleep -Seconds 5
$Script:chkbx1.content = "Test 1 Finished"
}
If ($chkbx2.IsChecked){
$Script:chkbx2.content = "Test 2 Running"
Start-Sleep -Seconds 5
$Script:chkbx2.content = "Test 2 Finished"
}
$Script:label2.content = "Tests Finished"
})
[void]$Script:label2
$async = $Form.Dispatcher.InvokeAsync({
$Form.ShowDialog() | Out-Null
})
$async.Wait() | Out-Null

WPF popup behavior

I am attempting to adapt this WPF Popup implementation to implement a messaging system. The goal is a popup any time I need to send a message, and the popup can be closed by the user by double clicking the message, and the message also goes away after a set time.
What I have now is this
using assembly PresentationFramework
using assembly System.Windows.Forms
using assembly System.Drawing
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$pshome\powershell.exe")
[xml]$xaml = '<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="window" WindowStyle="None" Height="200" Width="400"
ResizeMode="NoResize" ShowInTaskbar="False">
<Grid Name="grid" Background="#313130" Height="200" Width="400">
<Label Name="label" Content="Messanger Test" Foreground="White" FontSize="18" Margin="10,10,0,15"/>
<TextBox x:Name="Message" Height = "50" FontSize="18" Margin="10,10,0,15" />
</Grid>
</Window>'
$window = [Windows.Markup.XamlReader]::Load([System.Xml.XmlNodeReader]::New($xaml))
$window.Left = [System.Windows.SystemParameters]::WorkArea.Width-$window.Width
$window.Top = 0
$message = $Window.FindName('Message')
# Close the window if it's double clicked
$window.Add_MouseDoubleClick({
$window.Hide()
})
$messageCount = 1
do {
if ((Get-Random -Minimum:0 -Maximum:100) -le 30) {
$messageString = "($messageCount) $(Get-Date -format 'HH:mm:ss')"
$message.Text = $messageString
Write-Host $messageString
$messageCount ++
$window.Show()
Start-Sleep -s:10
$window.Hide()
}
Start-Sleep -s:5
} while ($messageCount -le 5)
This partially works, in that the first message pops up, and it will hide after 10 seconds. However, double clicking to hide doesn't work, nor do subsequent shows happen. I know the criteria are being met, as the console shows each new time message.
So...
What is wrong with my MouseDoubleClick event, and
What is keeping messages after the first from showing?
The Start-Sleep you are using makes the code wait while not processing other events, like the MouseDoubleClick.
In order to have a window keep responding and at the same time wait for a certain period of time, you need to add a System.Windows.Forms.Timer object.
This Timer has a Tick event in which you can stop it from running in order to proceed with the code.
I would suggest something like this:
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$pshome\powershell.exe")
[xml]$xaml = '<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="window" WindowStyle="None" Height="200" Width="400"
ResizeMode="NoResize" ShowInTaskbar="False">
<Grid Name="grid" Background="#313130" Height="200" Width="400">
<Label Name="label" Content="Messenger Test" Foreground="White" FontSize="18" Margin="10,10,0,15"/>
<TextBox x:Name="Message" Height = "50" FontSize="18" Margin="10,10,0,15" />
</Grid>
</Window>'
$window = [Windows.Markup.XamlReader]::Load([System.Xml.XmlNodeReader]::New($xaml))
$window.Left = [System.Windows.SystemParameters]::WorkArea.Width-$window.Width
$window.Top = 0
$window.Topmost = $true
$message = $Window.FindName('Message')
# create a Timer object to use instead of Start-Sleep
# old PowerShell: $timer = New-Object System.Windows.Forms.Timer
$timer = [System.Windows.Forms.Timer]::new()
$timer.Add_Tick({
Write-Host ">> Timer Tick.."
$timer.Stop()
})
# Close the window if it's double clicked
$window.Add_MouseDoubleClick({
Write-Host ">> Mouse Double Click.."
$timer.Stop()
})
$maxLoops = 5
for ($messageCount = 1; $messageCount -le $maxLoops; $messageCount++) {
$messageString = "($messageCount) $(Get-Date -format 'HH:mm:ss')"
$message.Text = $messageString
Write-Host $messageString
$window.Show()
# start the timer to fire after 10 seconds and then disable itself
$timer.Stop()
$timer.Interval = 10000
$timer.Start()
# while the Tick event did not occur, respond to other events
# such as e mouse double-click on the window
while ($timer.Enabled) { [System.Windows.Forms.Application]::DoEvents() }
# the timer tick event happened or the user double-clicked the window
$window.Hide()
# end of the loop reached, no use waiting some more..?
if ($messageCount -ge $maxLoops) { break }
# start the timer to fire after (random) 1 - 5 seconds
$interval = (Get-Random -Minimum 1 -Maximum 5)
$timer.Stop()
$timer.Interval = $interval * 1000
$timer.Start()
Write-Host ">> Loop wait $interval seconds.."
while ($timer.Enabled) { [System.Windows.Forms.Application]::DoEvents() }
}
# we're done, clean-up
$timer.Dispose()
$window.Close()

PowerShell WinForms prevents installing Windows Features

I'm trying to install IIS features via PowerShell but being prevented from doing so. If I run the code below, the script hangs with:
VERBOSE: Installation started...
VERBOSE: Continue with installation?
VERBOSE: Prerequisite processing started...
WARNING: The plug-in for "Web Server (IIS)" is taking more time to load than expected
And yet if I run the foreach loop from a console it just works. This leads me to believe something in the GUI is locking it up. Anyone have any ideas?
# Checks to make sure you're running the script in admin mode since the script cannot complete otherwise.
# Bombs out if not otherwise.
If ( !( [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ) ) {
Write-Host "You are not running in an elevated window" -f Red
Pause
Exit
}
# loads .NET framework for the GUI.
[void][System.Reflection.Assembly]::LoadWithPartialName( 'presentationframework' )
[void][System.Reflection.Assembly]::LoadWithPartialName( 'System.Windows.Forms' )
[xml]$xaml = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="SharePoint Deployment" Height="580" Width="400" >
<Grid Name="gridname" Background="#FF0070C9" Margin="0,0,0,0">
<Label Name="SetupLabel" Content="SharePoint Deployment script" FontSize="13" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="30,20,0,0" Height="30" Width="200" Foreground="White"
FontWeight="Bold" />
<Button Name="LoadIMG" Content="Load IMG File" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="255,22,0,0" Height="25" Width="100" />
<Label Name="PreReq" Content="Pre-Req checks/installs" FontSize="11" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="30,65,0,0" Height="25" Width="320" Foreground="White" />
<ProgressBar Name="PreReqProgressBar" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="30,95,0,0" Width="325" Height="25" />
<Label Name="LogLabel" Content="Log..." HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="11"
Margin="30,140,0,0" Height="25" Width="320" Foreground="White" />
<RichTextBox Name="LogReport" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="30,170,0,0"
Height="340" Width="325" VerticalScrollBarVisibility="Auto" >
<FlowDocument>
<Paragraph>
<Run Text=""/>
</Paragraph>
</FlowDocument>
</RichTextBox>
</Grid>
</Window>
'#
# Attempts to render the xaml code.
$reader = New-Object System.Xml.XmlNodeReader $xaml
# Converts xaml into WPF raw format. Bombs out if there's a problem.
Try {
$Form = [Windows.Markup.XamlReader]::Load( $reader )
}
Catch {
Write-Warning "Unable to parse XML, with error: `n$( $Error[0] )
`nHave you overwritten something? Drop your code into Visual Studio, it'll tell you where it's failing."
Throw
}
# This line here creates variables for everything in the XAML code above where a name exists, and prefixes "form"
# in front.
# i.e. <Button Name="GoButton"... becomes $formgobutton. Use $formgobutton | get-member to see variables of
# our newly created button.
$xaml.SelectNodes( "//*[#Name]" ) |
ForEach-Object { Set-Variable -Name "form$( $_.Name )" -Value $Form.FindName( $_.Name ) }
Function Write-RichTextBox {
<#
.SYNOPSIS
Makes changes to the fonts within the log box on a per amend basis.
.DESCRIPTION
Makes changes to the fonts within the log box on a per amend basis.
.EXAMPLE
Write-RichTextBox "Hello World" -colour Blue -Style Italic
The $FormLogReport richtext box will update with "Hello World" in Blue italic font.
.NOTES
$formLogReport.ScrollToEnd() will always place the scrollbar at the bottom of the richtext box when a
new line is added.
$form.Dispatcher.Invoke( [Action]{},[Windows.Threading.DispatcherPriority]::ContextIdle ) updates the
richtext box on the fly. It's a TRY/CATCH as if the box is refreshed too quickly you'll get errors.
#>
Param(
[string]$text,
[string]$colour = "Black",
[string]$style = "Normal"
)
Begin {
$RichTextRange = New-Object System.Windows.Documents.TextRange(
$formLogReport.Document.ContentEnd, $formLogReport.Document.ContentEnd )
}
Process {
If ( $style -eq "Italic" ) {
$RichTextRange.Text = "'$Text'"
}
else {
$RichTextRange.Text = $Text
}
$RichTextRange.ApplyPropertyValue( ( [System.Windows.Documents.TextElement]::ForegroundProperty ), $colour )
$RichTextRange.ApplyPropertyValue( ( [System.Windows.Documents.TextElement]::FontStyleProperty ), $style )
}
End {
$formLogReport.ScrollToEnd()
Try {
$form.Dispatcher.Invoke( [Action]{},[Windows.Threading.DispatcherPriority]::ContextIdle )
}
Catch { }
}
}
$windowsFeatures = ( "Web-Server", "Web-WebServer", "Web-Common-Http", "Web-Default-Doc", "Web-Static-Content",
"Web-Dir-Browsing", "Web-Http-Errors", "Web-App-Dev", "Web-Asp-Net", "Web-Asp-Net45", "Web-Net-Ext", "Web-Net-Ext45",
"Web-ISAPI-Ext", "Web-ISAPI-Filter", "Web-Health", "Web-Http-Logging", "Web-Log-Libraries", "Web-Request-Monitor",
"Web-Http-Tracing", "Web-Security", "Web-Basic-Auth", "Web-Windows-Auth", "Web-Filtering", "Web-Performance",
"Web-Stat-Compression","Web-Dyn-Compression", "Web-Mgmt-Tools", "Web-Mgmt-Console", "WAS", "WAS-Process-Model",
"WAS-NET-Environment", "WAS-Config-APIs", "Windows-Identity-Foundation", "NET-HTTP-Activation", "NET-Non-HTTP-Activ",
"NET-WCF-Pipe-Activation45", "NET-WCF-HTTP-Activation45","Xps-Viewer" )
$formLoadIMG.Add_Click( {
$formLogReport.Document.Blocks.Clear()
Import-Module Servermanager
Foreach ( $windowsFeature in $windowsFeatures ) {
Try {
$installwf = Install-WindowsFeature $windowsFeature -Confirm:$false -Restart:$false
Write-RichTextBox "Successfully installed: "
Write-RichTextBox $windowsFeature -style Italic
Write-RichTextBox `r
}
Catch {
Write-RichTextBox "Could not install $WindowsFeature" -colour Red
}
}
} )
$form.ShowDialog()
Not getting much help from Google so would really appreciate some feedback. Thanks
So thanks to #RohinSidharth, your comments backed up my theory the GUI was creating the problems.
I built a function to 'sandbox' the job but that wasn't enough, I still had to use Invoke-Command to get it working.
Function InstallWinFeatures {
$windowsFeatures = #( "Web-Server", "Web-WebServer", "Web-Common-Http", "Web-Default-Doc", "Web-Static-Content",
"Web-Dir-Browsing", "Web-Http-Errors", "Web-App-Dev", "Web-Asp-Net", "Web-Asp-Net45", "Web-Net-Ext", "Web-Net-Ext45",
"Web-ISAPI-Ext", "Web-ISAPI-Filter", "Web-Health", "Web-Http-Logging", "Web-Log-Libraries", "Web-Request-Monitor",
"Web-Http-Tracing", "Web-Security", "Web-Basic-Auth", "Web-Windows-Auth", "Web-Filtering", "Web-Performance",
"Web-Stat-Compression","Web-Dyn-Compression", "Web-Mgmt-Tools", "Web-Mgmt-Console", "WAS", "WAS-Process-Model",
"WAS-NET-Environment", "WAS-Config-APIs", "Windows-Identity-Foundation", "NET-HTTP-Activation", "NET-Non-HTTP-Activ",
"NET-WCF-Pipe-Activation45", "NET-WCF-HTTP-Activation45","Xps-Viewer" )
foreach ( $Feature in $windowsFeatures ) {
Try {
Write-RichTextBox "trying to install $feature`r"
Invoke-Command -ComputerName localhost -ScriptBlock { Install-WindowsFeature -Name $using:Feature -IncludeManagementTools }
Write-RichTextBox "Installed $feature`r"
}
Catch {
Write-RichTextBox "Unable to remove $feature`r"
}
}
}
If anyone has any idea why this is happening I'm all ears.
Thanks

Using powershell and WPF how to display a selected directory path in a list box

I found this Powershell function code block to choose a folder. Then, I added two variables (sourcePath, source) to reference to a XAML file. When, user presses "OK", I need the selected folder path to be shown in a different list box,"$sourcePath". I tried calling the list box as " $sourcePath.Write($objForm.SelectedPath)" after the "Return $objForm.SelectedPath". I'm doing something wrong, but don't know how to fix it. Thank you.
$XAML = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Choose Folder" Height="350" Width="500">
<StackPanel>
<Button x:Name="choose" Content="choose" HorizontalAlignment="Left"
Margin="42,108,0,0" VerticalAlignment="Top" Width="121" />
<ListBox x:Name="sourcePath" HorizontalAlignment="left" Height="45"
Margin="42,120,0,0" VerticalAlignment="Top" Width="400"/>
</StackPanel>
</Window>
'#
$sourcePath=$win.Find("sourcePath")
$source=$win.Find("source")
$source.Add_click({Select-FolderDialog})
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
Function Select-FolderDialog
{param([string]$Description="Select Folder",[string]$RootFolder="Desktop")
$objForm = New-Object System.Windows.Forms.FolderBrowserDialog
$objForm.Rootfolder = $RootFolder
$objForm.Description = $Description
$Show = $objForm.ShowDialog()
If ($Show -eq "OK")
{
Return $objForm.SelectedPath
$sourcePath.Write($objForm.SelectedPath)
}
Else
{
Write-Error "Operation cancelled by user."
}
$b = Select-FolderDialog
}
There were only a few minor things wrong like using "write" for putting something into a ListBox.
And of course, like Frode F. pointed out, ommit the return - there is no need for a return statement inside a function except for leaving the function before it ends.
Here is a slightly modified version of your script code that display a window with a listbox and button and puts the selected path into that listbox.
$XAML = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Folder-Browser"
Height="500"
Width="600"
>
<StackPanel>
<ListBox x:Name="sourcePath" Height="300" Width="320" Margin="10"/>
<Button x:Name="choose" Content="Choose Folder" Width="120" Height="40" Margin="10"/>
</StackPanel>
</Window>
'#
$Win = [Windows.Markup.XamlReader]::Parse($XAML)
$sourcePath = $Win.FindName("sourcePath")
$button = $Win.FindName("choose")
$button.Add_Click({Select-FolderDialog})
Add-Type -Assembly System.Windows.forms
function Select-FolderDialog
{
param([String]$Description="Select Folder",
[String]$RootFolder="Desktop")
$objForm = New-Object System.Windows.Forms.FolderBrowserDialog
$objForm.Rootfolder = $RootFolder
$objForm.Description = $Description
$Show = $objForm.ShowDialog()
if ($Show -eq "OK")
{
$SourcePath.Items.Add($objForm.SelectedPath)
}
}
$Win.ShowDialog()
If the Script runs with PowerShell 4.0 and above I recommend the parse method which returns the Window object right away:
$Win = [Windows.Markup.XamlReader]::Parse($Xaml)
Remove Return $objForm.SelectedPath. It returns the value to the console and ends the function which means that $sourcePath.Write($objForm.SelectedPath) is never executed.
The Return keyword exits a function, script, or script block
Source: about_Return

Changing System Colors

Thank you for taking the time to help me.
I am using PowerShell to build a GUI and I would like to override default system colors.
For example, when a control is highlighted (TextBox or ComboBox), form shows system colors. I would like to change the color to use AliceBlue. So far I have tried following codes, but to no avail:
[System.Drawing.SystemColors]::Highlight = 'AliceBlue'
[System.Drawing.SystemColors]::HighlightText = 'AliceBlue'
[System.Drawing.SystemColors]::ScrollBar = 'AliceBlue'
[System.Drawing.SystemColors]::Control = 'AliceBlue'
[System.Drawing.SystemColors]::HotTrack = 'AliceBlue'
[System.Drawing.SystemColors]::Window = 'AliceBlue'
[System.Drawing.SystemColors]::WindowFrame = 'AliceBlue'
The documentation says that those properties you are trying to set are readonly.
You can do this by invoking the user32.dll SetSysColors function:
$signature = #'
[DllImport("user32.dll")]
public static extern bool SetSysColors(
int cElements,
int [] lpaElements,
uint [] lpaRgbValues);
'#
$type = Add-Type -MemberDefinition $signature `
-Name Win32Utils `
-Namespace SetSysColors `
-PassThru
$color = [Drawing.Color]::AliceBlue
# For RGB color values:
# $color = [Drawing.Color]::FromArgb(255,255,255)
$elements = #('13')
$colors = [Drawing.ColorTranslator]::ToWin32($color)
$type::SetSysColors($elements.Length, $elements, $colors)
Where the 13 element represents COLOR_HIGHLIGHT, which is the colour of an item selected in a control.
After running the above code, here is the result:
ComboBox
TextBox
You can see that the colour of the actual text has changed and it is barely visible. To change this, simply run:
$color = [Drawing.Color]::Black
$elements = #('14')
$colors = [Drawing.ColorTranslator]::ToWin32($color)
$type::SetSysColors($elements.Length, $elements, $colors)
Where 14 represents COLOR_HIGHLIGHTTEXT, which is the colour of the text of an item selected in a control.
To see more about SetSysColors check PInvoke. Also, go here to find more color codes.
I don't know if it is possible to set the highlight colour for only the PowerShell GUI and nothing else using WinForms or SetSysColor, but one way you might consider is by using a WPF TextBox instead of WinForms. This way you can use SelectionBrush and SelectionOpacity:
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="Initial Window"
WindowStartupLocation = "CenterScreen"
ResizeMode="NoResize"
SizeToContent = "WidthAndHeight"
ShowInTaskbar = "True"
Background = "lightgray">
<StackPanel >
<Label Content='Type in this textbox' />
<TextBox x:Name="InputBox"
Height = "50"
SelectionBrush= "Green"
SelectionOpacity = "0.5" />
</StackPanel>
</Window>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )
$Window.ShowDialog() | Out-Null

Resources