Update Textbox if var changes - powershell

I want to update my WPF PowerShell GUI. I set up a small GUI with one button and one textbox to test my script. It looks like:
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
Add-Type -AssemblyName System.Windows.Forms
[xml]$XAML = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="test" Height="630" Width="500" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid>
<StackPanel>
<Button Name="test" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<TextBox Name="write" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
</StackPanel>
</Grid>
</Window>
"#
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
try {
$Form = [Windows.Markup.XamlReader]::Load($reader)
} catch {
Write-Host "Something is wrong"; exit
}
$xaml.SelectNodes("//*[#Name]") | %{
Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)
}
$test.Add_Click({
Write-Host $varvar
Start-Process powershell -Argument ".\varchecker.ps1"
})
$Form.ShowDialog() | Out-Null
By clicking the button I start another script where I read a variable. This looks like:
while ($true) {
$global:varvar = Get-Content var.txt
Write-Host $varvar
}
Now I want to print the var from the started script to the textbox in the other script. Is this possible? Or the better question: is that a good way to do that?

I don't think you can do it this way. You are trying to start a new PowerShell process, getting some data and passing it to the parent process. I think you can sort of do it in C with pipe() and fork() but havent found a way to do it in PowerShell yet.
You can however, use Start-Job, which creates a process in the background. Alternatively, you can look at Runspaces too for multithreading.
You can also use Invoke-Expression to call the script.
All you have to do from the script is return $Varvar.
If you can tell us what problem you are trying to solve with this method, maybe folks can be more helpful.

Getting values from a new PowerShell process inside your GUI is much too complicated if all you want is to read the contents of a file.
Why not do it all from the GUI code you have?
# [System.Reflection.Assembly]::LoadWithPartialName is deprecated.
Add-Type -AssemblyName PresentationFramework
[xml]$xaml = #"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="test" Height="630" Width="500" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Grid>
<StackPanel>
<Button Name="test" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<TextBox Name="write" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
</StackPanel>
</Grid>
</Window>
"#
$reader = New-Object System.Xml.XmlNodeReader $xaml
try {
$Form = [Windows.Markup.XamlReader]::Load($reader)
}
catch {
Write-Host "Something is wrong"; exit
}
# get references for the button and textbox objects into PowerShell variables.
$button = $Form.FindName("test")
$textBox = $Form.FindName("write")
$button.Add_Click({
# don't start a new PowerShell process, but simply read the file here
$textBox.Text = $var = Get-Content -Path 'PATH TO var.txt' -Raw
Write-Host $var
})
$Form.ShowDialog() | Out-Null
# clean-up
$reader.Dispose()

Related

Powershell + XAML + Search Box + capture and display

I am just about finished with program and need some help getting over the finish line. What is supposed to do is search through log files in a predefined directory. Nothing happens with the text I enter in the search box nor when I use the button that I created to execute the search. I have searched all over the Internet which has me as far as I am. I see lot's of examples in Winforms but I don't know the "translation" between Winforms and WPF.
I think that if someone can get me passed this hurdle I can implement the other features I want to add.
PS: I really have no programming skills, just tenacity.
Thanks in advance to all who help.
'''
# Import needed Assemblies
Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase,
System.Drawing, System.Windows.Forms, WindowsFormsIntegration
[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
[Windows.Forms.Application]::EnableVisualStyles()
#XAML form designed using Visual Studio
# Build the GUI
[xml]$Form = #"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Log Crawler"
HorizontalAlignment="Center"
FontSize="11" Height="700" Width="1300" FontFamily="Calibri"
WindowStartupLocation="CenterScreen"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled" WindowStyle="ThreeDBorderWindow">
<Grid Margin="0,0,0,0">
<Grid.Background>
<LinearGradientBrush EndPoint="0.8,1" StartPoint="0.8,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<TextBox x:Name="IntegrationsLogCrawler" Text="Integration Log Crawler"
HorizontalAlignment="Center" VerticalAlignment="Top" TextAlignment="Center" TextWrapping="NoWrap"
Margin="0,20,0,0 " Height="37" Width="260"
FontSize="24" FontFamily="Calibri" FontWeight="Bold" FontStyle="Normal" FlowDirection="LeftToRight"/>
<GroupBox x:Name="SearchBox" Header="SearchBox"
HorizontalAlignment="Center" VerticalAlignment="Top"
FontSize="12" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFE4E41A"
Margin="0,100,0,0" Height="40" Width="400">
<TextBox x:Name="InputBox" TextWrapping="NoWrap"/>
</GroupBox>
<Button x:Name="LogCrawl" Content="Log Crawl"
HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="0,150,0,0" Height="40" Width="190"
FontSize="18" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFEE0C3F"/>
<Button x:Name="Exit" Content="Exit"
HorizontalAlignment="Center" VerticalAlignment="Top"
Margin="0,200,0,0" Height="39" Width="110"
FontSize="16" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFE4E41A" Background="#FF040404"/>
<GroupBox x:Name="Results" Header="Results"
HorizontalAlignment="Center" VerticalAlignment="Top"
FontSize="12" FontFamily="Calibri" FontWeight="Bold" Foreground="#FFE4E41A"
Margin="0,250,0,0 " Height="250" Width="1000">
<TextBox x:Name="OutputBox" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
</GroupBox>
</Grid>
</Window>
"#
#Create a form
$XMLReader = (New-Object System.Xml.XmlNodeReader $Form)
$XMLForm = [Windows.Markup.XamlReader]::Load($XMLReader)
#Connect to Controls
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
New-Variable -Name $_.Name -Value $XMLForm.FindName($_.Name) -Force
}
# Load Log Crawl Menu Button Controls
$InputBox = $XMLForm.FindName('InputBox')
$OutputBox = $XMLForm.FindName('OutputBox')
$LogCrawl = $XMLForm.FindName('LogCrawl')
$Exit = $XMLForm.FindName('Exit')
# Logfile Location
$LogFiles = "$($ENV:USERPROFILE)\Documents\_data\logfiles"
# Log Crawl Button Action
$LogCrawl1.Add_Click({
Select-String -Path "$LogFiles\*.*" -Pattern $InputBox -Context 10, 20 | Write-Host
})
# EXIT
$Exit.Add_Click({
$XMLForm.Close()
})
#Show XMLform
$null = $XMLForm.ShowDialog()
'''
I'm not sure about the workings of this part (gave me exception "You cannot call a method on a null-valued expression.") :
#Connect to Controls
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
New-Variable -Name $_.Name -Value $XMLForm.FindName($_.Name) -Force
}
and I think you should remove it.
Then you made a typo in the button variable name $LogCrawl1 --> $LogCrawl, but also change the Add_Click() method to:
# Log Crawl Button Action
$LogCrawl.Add_Click({
# Logfile Location
$LogFiles = "$($ENV:USERPROFILE)\Documents\_data\logfiles"
$OutputBox.Text = Select-String -Path "$LogFiles\*.*" -Pattern $InputBox.Text -SimpleMatch -Context 10, 20
})
P.S. It is unclear if this is also in your real code, but the final closing "# for the XAML here-string must not have whitespace to the left.

PowerShell XAML ListBox Add_DoubleClick

Quick one. I want to add Add_DoubleClick() to ListBox in PowerShell, but this doesn't work with implemented ListBox via XAML file... and I'm stuck
I will be very grateful for any help here...
I still have msg warning, that my post is mostly code, so I need to write something here...
# $ErrorActionPreference= 'silentlycontinue'
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$AssemblyLocation = Join-Path -Path $ScriptPath -ChildPath .\themes
foreach ($Assembly in (Dir $AssemblyLocation -Filter *.dll)) {
[System.Reflection.Assembly]::LoadFrom($Assembly.fullName) | out-null
}
[xml]$xaml = #"
<Controls:MetroWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="ddd"
Height="500"
Width="800"
BorderThickness="0"
GlowBrush="{DynamicResource AccentColorBrush}"
ResizeMode="CanResizeWithGrip"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Cyan.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.AnimatedTabControl.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
</ResourceDictionary.MergedDictionaries>
</Window.Resources>
<Grid>
<ListBox SelectionMode="Single" ItemsSource="{Binding}" x:Name="ListBox" HorizontalAlignment="Right" Height="100" Margin="244,144,0,0" VerticalAlignment="Top" Width="112">
<ListBoxItem Content="Coffie"></ListBoxItem>
</ListBox>
</Grid>
</Controls:MetroWindow>
"#
#Read the form
$Reader = (New-Object System.Xml.XmlNodeReader $xaml)
$Form = [Windows.Markup.XamlReader]::Load($reader)
#AutoFind all controls
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object {
New-Variable -Name $_.Name -Value $Form.FindName($_.Name) -Force
}
$ListBox.Items.AddRange()
$ListBox.Add_DoubleClick({
$TextBox2.AppendText("dddd`r`n")
})
...`
I used different approach to my problem:
$smth.Add_IsMouseCapturedChanged({
$smth.SelectedIndex = -1
})

XAML to powershell

Im a beginner and is needing help running an XAML into powershell.
Im trying to follow some format from the net but cant seem to convert my xaml GUI to powershell.
Any help will do. Heres my code:
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
Title="Strike Email" Height="137" Width="274" Background="White">
<Grid Background="#FF005D80">
<Button Content="1st Strike" HorizontalAlignment="Left" Height="50" Margin="10,30,0,0" VerticalAlignment="Top" Width="70" Background="White" Click="Button_Click"/>
<Button Content="3rd Strike" HorizontalAlignment="Left" Height="50" Margin="180,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
<Button Content="2nd Strike" HorizontalAlignment="Left" Height="50" Margin="95,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
</Grid>
For that, you will need to:
Cast your xaml string as a xml
Load presentationframework assembly
Load the XAML into a xmlNodeReader
Load the form
Powershell must be running in STA (Single threaded) mode for this to work
(Powershell ISE launch in STA mode by default.)
[xml]$xaml = 'ValidXAMLStringHere'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )
$Form.ShowDialog() | out-null
Something like the snippet would be the end result.
I even added a button click event for fun.
[xml]$xaml = #'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Strike Email" Height="137" Width="274" Background="White">
<Grid Background="#FF005D80">
<Button Content="1st Strike" HorizontalAlignment="Left" Height="50" Margin="10,30,0,0" VerticalAlignment="Top" Width="70" Background="White" x:Name="FirstStrike" />
<Button Content="3rd Strike" HorizontalAlignment="Left" Height="50" Margin="180,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
<Button Content="2nd Strike" HorizontalAlignment="Left" Height="50" Margin="95,30,0,0" VerticalAlignment="Top" Width="70" Background="White"/>
</Grid>
</Window>
'#
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try
{
$Form=[Windows.Markup.XamlReader]::Load( $reader )
}
catch
{
Write-Host "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."t
}
cls
$Button = $Form.FindName('FirstStrike')
$Button.Add_Click({ Write-Host "First Strike Button Clicked" -ForegroundColor Cyan})
$Form.ShowDialog() | out-null
References:
Technet - Integrating XAML into PowerShell
Learn Powershell - PowerShell and WPF: Buttons

How can i incorporate a progress bar into my PowerShell robocopy?

i recently made a robocopy function with PowerShell so i can transfer files over from one computer to another. However, i cannot figure out how to incorporate a progress bar into my script since i did it a bit differently. Here is an excerpt from the script:
$inputXML = #"
<Window x:Class="DataTransferV2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataTransferV2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Background="#FF0D94D2">
<Grid>
<Label x:Name="head_label" Content="Device Data Transfer" HorizontalAlignment="Left" Margin="151,10,0,0" VerticalAlignment="Top" FontSize="18" FontFamily="Segoe UI Black"/>
<Button x:Name="checkall_button" Content="Check All" HorizontalAlignment="Left" Margin="212,49,0,0" VerticalAlignment="Top" Width="75"/>
<CheckBox x:Name="documents_checkBox" Content="Documents" HorizontalAlignment="Left" Margin="57,102,0,0" VerticalAlignment="Top"/>
<Button x:Name="start_button" Content="Start" HorizontalAlignment="Left" Margin="89,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
<Button x:Name="cancel_button" Content="Cancel" HorizontalAlignment="Left" Margin="205,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
<Button x:Name="exit_button" Content="Exit" HorizontalAlignment="Left" Margin="320,246,0,0" VerticalAlignment="Top" Width="88" Height="36"/>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$XAML = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
#===========================================================================
# Transfer Documents
#===========================================================================
Function global:Documents_Transfer{
if($WPFdocuments_checkBox.IsChecked -eq $true){
Write-Host "Transferring Documents..."
$quotation = ":"
$source = "$DLetter$quotation\Users\$associd\Documents"
$destination = "C:\Users\$env:USERNAME\Documents"
$copy = "/COPY:DTO"
$files = "*.*"
$backup = "/B"
$reportextrafiles = "/X"
$verboseoutput = "V"
$fullpathname = "FP"
$mirror = "/MIR"
$backupmode = "/ZB"
$subfolders = "/E"
$Retries = "/R:2"
$consolewrite = "/TEE"
robocopy $source $destination $files $backup $reportextrafiles $verboseoutput $fullpathname $mirror $backupmode $subfolders $Retries $consolewrite
Write-Host "Documents Transferred"
}
}
$WPFstart_button.Add_Click({
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$associd = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the associate ID:", "Verification", "")
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
$DLetter = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the external drive letter:", "Drive", "")
Documents_Transfer
})
#===========================================================================
# Closes the form
#===========================================================================
$WPFexit_button.Add_Click({
$Form.Close()
})
#===========================================================================
# Shows the form
#===========================================================================
$Form.ShowDialog() | out-null
Currently i just have a write-host that attempts to track some progress. Can anyone offer advice as to how i can track this progress? Thanks in advanced!

Do Jobs really work in background in powershell?

I am trying to implement background commands that would initiate after the user clicks a button, and so far I have always ended up with my UI locking up while the job is on-going. I am currently using a somewhat time consuming for loop to check if my UI locks up. What can I do to let the job work in the background as the UI is freed up. I am not sure I want to complicate the code by adding runspaces. What am I doing incorrectly?
$inputXML = #"
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="Create User" Height="448.05" Width="656.017" ResizeMode="NoResize">
<Grid Margin="0,0,-6.8,-0.8" Background="#FFD7D7D7">
<Grid.RowDefinitions>
<RowDefinition Height="403*"/>
<RowDefinition Height="18*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="Create_User" Content="Create User" HorizontalAlignment="Left" Margin="67,224,0,0" VerticalAlignment="Top" Width="300" Height="26" RenderTransformOrigin="0.551,-0.671" IsEnabled="True">
<Button.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF3F3F3" Offset="0"/>
<GradientStop Color="#FFEBEBEB" Offset="0.5"/>
<GradientStop Color="#FFDDDDDD" Offset="0.5"/>
<GradientStop Color="#FFCDCDCD" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
</Button>
<Label x:Name="fname" Content="" HorizontalAlignment="Left" Margin="43,11,0,0" VerticalAlignment="Top" Height="26" Width="10"/>
<Label x:Name="fname1" Content="First Name" HorizontalAlignment="Left" Margin="88,54,0,0" VerticalAlignment="Top" Width="72" RenderTransformOrigin="0.513,1.469" Height="26"/>
<Label x:Name="lname" Content="Last Name" HorizontalAlignment="Left" Margin="88,83,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.167,-0.458" Width="72" Height="25"/>
<TextBox x:Name="fnametxt" Height="23" Margin="167,54,0,0" TextWrapping="Wrap" VerticalAlignment="Top" RenderTransformOrigin="0.501,0.452" HorizontalAlignment="Left" Width="136"/>
<Button x:Name="exitbtn" Content="Exit" HorizontalAlignment="Left" VerticalAlignment="Top" Width="135" Margin="447,365,0,0" Height="38" RenderTransformOrigin="0.489,0.462"/>
<Label x:Name="label" Content="Password" HorizontalAlignment="Left" Margin="92,113,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.064,0.601" Height="26" Width="68"/>
<PasswordBox x:Name="passwordBox" Margin="167,113,0,0" VerticalAlignment="Top" Height="24" HorizontalAlignment="Left" Width="136"/>
<TextBox x:Name="stsbox" HorizontalAlignment="Left" Height="62" Margin="67,267,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" Background="#FFDADADA" Foreground="Black" Opacity="0.45" SelectionBrush="#FF1D6EBF" RenderTransformOrigin="0.503,-0.59" IsReadOnly="True"/>
<TextBox x:Name="lnametxt" Height="23" Margin="167,85,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="136"/>
</Grid>
</Window>
"#
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[xml]$xaml = $inputXML
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."}
$xaml.SelectNodes("//*[#Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -Scope Global}
Function global:Get-FormVariables{
if ($global:ReadmeDisplay -ne $true) {$global:ReadmeDisplay=$true}
#write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
$WPFCreate_User.Add_Click({
$test = { for($i=1; $i -le 100000; $i++){
$z = $i + $z
$z
}}
$job = Start-Job $test
Wait-Job $job
Receive-Job $job -OutVariable results
Remove-Job $job
$WPFstsbox.text = "$results`n"
})
$WPFexitbtn.add_click({
$form.Close() | out-null
exit})
$form.ShowDialog() | Out-null
Yes, PSJobs are truly "background" jobs.
When you call Start-Job, a separate process is started, executing your job/command.
In your script, the job itself doesn't block the calling thread, but your subsequent command (Wait-Job $job) does.
If you simply fire off Start-Job and return from the Click handler, your UI wouldn't lock up:
$button.add_Click({
$jobCode = { Start-Sleep -Seconds 10 }
$job = Start-Job $jobCode
})
you'll see that the UI becomes responsive again in way lees than 10 seconds.
The problem is that you no longer have access to the $job and no longer control over when it is to be displayed.
To compensate for this, you need background thread or a timed event that can periodically check up on the job(s) for you and output the result.
You could use a Timer and PowerShell's builtin eventing infrastructure for this:
# Create timer
$backgroundTimer = New-Object System.Timers.Timer
# Set interval (ms)
$backgroundTimer.Interval = 500
# Make sure timer stops raising events after each interval
$backgroundTimer.AutoReset = $false
# Have powershell "listen" for the event in the background
Register-ObjectEvent $backgroundTimer -EventName 'Elapsed' -SourceIdentifier 'timerElapsed' -Action {
# Loop through any completed jobs with data to return, from "oldest" to "newest"
while(($finishedJob = Get-Job |Where-Object {$_.State -eq 'Completed' -and $_.HasMoreData}|Select-Object -First 1))
{
# Update the text box on your WPF form with the results
# NOTE: the event is executed in a separate scope, thus the "global:" scope prefix
$global:WPFstsbox.Text = "$(Receive-Job $finishedJob)`n"
# Clean up the job
Remove-Job $finishedJob
}
# Restart timer
$backgroundTimer.Start()
}
# Enable the timer
$backgroundTimer.Enabled = $true