Issue
I’m having trouble with having a WPF-window do continues update from PowerShell, and also not freeze.
The idea is that when I click the button, some function is to be run that updates the value for the progressbar. But which ever method or guide I use, I can’t get it to work. The progressbar is being updated and is filled to 100% when the task is done. But during the operation, the window freezes. The only way I can have a window that is not freezing, is if I do a simple value addition in the updateblock
. But I need to be able to run different functions and update the progressbar again and again.
What do I need to do to run a function or scriptblock from the Click-event so the window doesn’t freeze and the progressbar updates?
PowerShell-code:
Add-Type -Assembly PresentationFramework
$syncHash = [hashtable]::Synchronized( @{} )
$syncHash.Counter = 0.0
$syncHash.Do = {
param ( $syncHash, $ToDo )
$rsR = [runspacefactory]::CreateRunspace()
$rsR.ApartmentState = "STA"
$rsR.ThreadOptions = "ReuseThread"
$rsR.Open()
$rsR.SessionStateProxy.SetVariable( "syncHash", $syncHash )
$c = [powershell]::Create().AddScript( $ToDo )
$c.Runspace = $rsR
$h = $c.BeginInvoke()
do { start-sleep -millisecond 10 } until ( $h.IsCompleted -eq $true )
$c.EndInvoke( $h )
$rsR.Close()
$rsR.Dispose()
}
$syncHash.Window = [Windows.Markup.XamlReader]::Load( ( New-Object System.Xml.XmlNodeReader ( [xml]( Get-Content .\tt.xml ) ) ) )
$syncHash.Button = $syncHash.Window.FindName( "Button" )
$syncHash.Progress = $syncHash.Window.FindName( "Progress" )
$Timer = New-Object System.Windows.Threading.DispatcherTimer
$Task = { 1..100 | % { start-sleep -millisecond 10; $syncHash.Counter = $_ } }
$syncHash.Button.Add_Click( { Something } )
$syncHash.Window.Add_SourceInitialized( {
$Timer.Start()
$Timer.Add_Tick( $UpdateBlock )
$Timer.Interval = [TimeSpan]"0:0:0.01"
} )
function Something
{
& $syncHash.Do $syncHash $Task
}
$UpdateBlock =`
{
$syncHash.Window.Resources["Progress"] = [double]$syncHash.Counter
$syncHash.Window.Resources["PbText"] = "$( $syncHash.Counter ) %"
}
[void]$syncHash.Window.ShowDialog()
XAML-code:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Topmost="True"
WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight">
<Window.Resources>
<system:Double x:Key="Progress">0.0</system:Double>
<system:String x:Key="PbText"></system:String>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Click" Name="Button"/>
<Grid Grid.Row="1">
<ProgressBar Name="Progress" Minimum="0" Value="{DynamicResource Progress}" Width="230"/>
<TextBlock Text="{DynamicResource PbText}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
</Window>
Solution
I finally found what to do. Hade to rethink some stuff, and removed the runspace functionality all together.
Thanks to binding, it all got simpler.
Here is what I came to, also added binding for maxvalue of the progressbar:
Add-Type -Assembly PresentationFramework
$syncHash = @{}
$syncHash.Window = [Windows.Markup.XamlReader]::Load( ( New-Object System.Xml.XmlNodeReader ( [xml]( Get-Content .\tt.xml ) ) ) )
$syncHash.Button = $syncHash.Window.FindName( "Button" )
$syncHash.Progress = $syncHash.Window.FindName( "Progress" )
$Something = { param( $d )
function SomethingElse { gci }
$d[1] = ( SomethingElse ).Count
[system.windows.messagebox]::show($d[1])
foreach ( $i in ( 1..$d[1] ) ) { Start-Sleep -MilliSeconds 300; $d[0] = [double]( ( $i/$d[1] ) * 100 ) }
}
$syncHash.Button.Add_Click( { ( [powershell]::Create().AddScript( $Something ).AddArgument( $DataContext ) ).BeginInvoke() } )
$DataContext = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
$DataContext.Add( 0.0 )
$DataContext.Add( 0.0 )
$syncHash.Progress.DataContext = $DataContext
$Binding = New-Object System.Windows.Data.Binding -ArgumentList "[0]"
$Binding.Mode = [System.Windows.Data.BindingMode]::OneWay
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Progress, [System.Windows.Controls.ProgressBar]::ValueProperty, $Binding )
$Binding = New-Object System.Windows.Data.Binding -ArgumentList "[1]"
$Binding.Mode = [System.Windows.Data.BindingMode]::OneWay
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Progress, [System.Windows.Controls.ProgressBar]::MaximumProperty, $Binding )
[void]$syncHash.Window.ShowDialog()
Answered By – Smorkster
Answer Checked By – Robin (BugsFixing Admin)