Powershell Progress Bar on Windows Forms

I am trying to add a progress indicator to a form in powershell. I don’t want to use the PowerShell Write-Progress cmdlet (because when I run the script from the command line, it shows a text-based execution line, and I always need a panel based on the form / graphic).

I tried this and it seemed to work (found on the Internet):

[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null [reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null $form_main = New-Object System.Windows.Forms.Form $progressBar1 = New-Object System.Windows.Forms.ProgressBar $timer1 = New-Object System.Windows.Forms.Timer $timer1_OnTick = { $progressBar1.PerformStep() } $form_main.Text = 'ProgressBar demo' $progressBar1.DataBindings.DefaultDataSourceUpdateMode = 0 $progressBar1.Step = 1 $progressBar1.Name = 'progressBar1' $form_main.Controls.Add($progressBar1) $timer1.Interval = 100 $timer1.add_tick($timer1_OnTick) $timer1.Start() $form_main.ShowDialog()| Out-Null 

However, I do not want the event to update the progress bar (as $ timer1_OnTic does in the example above). I want to update it myself by making calls in my entire script, for example:

 $progressBar1.PerformStep() 

or

 $progressBar1.Value = 10 

I think I need some kind of background worker that updates the progress bar when I make calls on PerformStep () or change the value of progressBar

Calling ShowDialog stops all processing inside the script until the form is closed.

+5
source share
3 answers

If I understand correctly, you can change ShowDialog () to Show (), which displays a dialog box without blocking your script. Then you can continue to run and update the progress bar.

You may be disappointed with the lack of interactivity of the form.

+6
source

The method with which I have had some success is to use a child workspace for the GUI (in this case WPF), so it does not block the script. Access to the data can be obtained both in the parent and in the subprocess through the session proxy server.

eg.

 # define the shared variable $sharedData = [HashTable]::Synchronized(@{}); $sharedData.Progress = 0; $sharedData.state = 0; $sharedData.EnableTimer = $true; # Set up the runspace (STA is required for WPF) $rs = [RunSpaceFactory]::CreateRunSpace(); $rs.ApartmentState = "STA"; $rs.ThreadOptions = "ReuseThread"; $rs.Open(); # configure the shared variable as accessible from both sides (parent and child runspace) $rs.SessionStateProxy.setVariable("sharedData", $sharedData); # define the code to run in the child runspace $script = { add-Type -assembly PresentationFramework; add-Type -assembly PresentationCore; add-Type -assembly WindowsBase; [xml]$xaml = @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" MaxHeight="100" MinHeight="100" Height="100" MaxWidth="320" MinWidth="320" Width="320" WindowStyle="ToolWindow"> <Canvas Grid.Row="1"> <TextBlock Name="ProgressText" Canvas.Top="10" Canvas.Left="20">Hello world</TextBlock> <ProgressBar Name="ProgressComplete" Canvas.Top="30" Canvas.Left="20" Width="260" Height="20" HorizontalAlignment="Center" Value="20" /> </Canvas> </Window> "@ # process the xaml above $reader = New-Object System.Xml.XmlNodeReader $xaml; $dialog = [Windows.Markup.XamlReader]::Load($reader); # get an handle for the progress bar $progBar = $dialog.FindName("ProgressComplete"); $progBar.Value = 0; # define the code to run at each interval (update the bar) # DON'T forget to include a way to stop the script $scriptBlock = { if ($sharedData.EnableTimer = $false) { $timer.IsEnabled = $false; $dialog.Close(); } $progBar.value = $sharedData.Progress; } # at the timer to run the script on each 'tick' $dialog.Add_SourceInitialized( { $timer = new-Object System.Windows.Threading.DispatherTimer; $timer.Interface = [TimeSpan]"0:0:0.50"; $timer.Add_Tick($scriptBlock); $timer.Start(); if (!$timer.IsEnabled) { $dialog.Close(); } }); # Start the timer and show the dialog &$scriptBlock; $dialog.ShowDialog() | out-null; } $ps = [PowerShell]::Create(); $ps.Runspace = $rs; $ps.AddScript($script).BeginInvoke(); # if you want data from your GUI, you can access it through the $sharedData variable Write-Output $sharedData; 

If you try this code as soon as the dialog box appears, you can change the progress bar by setting the value to $sharedData.Progress

This allowed me to write many dialogs for tools, I am limited to our infrastructure for using powershell from within the workspace, and WPF seems to work much better than forms.

+3
source

Take a look at the chic progress bar , it has a horizontal, vertical and circular progress bar.

0
source

Source: https://habr.com/ru/post/1347830/


All Articles