Menu

Virtual Geek

Tales from real IT system administrators world and non-production environment

Powershell WPF custom Image based ProgressBar

In this article I am showing some different ProgressBars for WPF GUI, This progressbar can show your chunk of silhouette image loading instead progress nicely. Below is the demo for my new progressbars, It can be used as an animated text or image. Images should be silhouette, A silhouette is the image of a person, animal, object or scene represented as a solid shape of a single color, usually black, with its edges matching the outline of the subject. The interior of a silhouette is featureless, and the whole is typically presented on a light background, usually white, or none at all.

As start, I need image first and created my own text silhouette image using Microsoft PowerPoint.

  1. In the PowerPoint Add a Text Box from shapes.
  2. Change Text color to black, do not keep any margin for text in the box, it should be as tight as possible, Choose your favorite font and size.
  3. Box background fill should be none
  4. Select outline of textbox to none
  5. Type your text
  6. Right click text box and from context menu choose Save as Picture. Save it as png extension This is critical step, (Need only Png, as this file formats can retain background transparency).
  7. View and Verify your file in photo viewer, It looks good to me, using above method I created few more images.

Microsoft Powershell with Powerpoint textbox wpf gui Progressbar image based customised progressbar windows presentation platform scripting

I have created xaml gui form using Visual Studio, Added ProgressBar, TextBlock and Button controls to it, There is little change I made to the ProgressBar as shown below. 

        <ProgressBar Name="ProgressBar1" HorizontalAlignment="Left" Height="41" VerticalAlignment="Top" Width="264" Margin="10,10,0,0" Background="{x:Null}">
            <ProgressBar.OpacityMask>
                <ImageBrush ImageSource="C:\Temp\Completed.png"/>
            </ProgressBar.OpacityMask>

        </ProgressBar>

Settings on ProgressBar background (color) is set to null or the color you want, unclosed it in the last, Added ProcessBar.OpacityMask property with ImageSource as earlier created png file and in the last close ProgressBar property.

Microsoft Powershell wpf Windows presentation frameworks, WIndow xml xaml form grid New progressbar opacitymask, imagesource background null.png

As to show progressbar correctly on wpf powershell, I am using runspaces in the script. This url link by Travor Jones has nice guide to understand powershell runspace correctly.Without runspaces my script will hung and unresponsive, Doesn't show animatation progress and freezes, directly shows 100% progress once all processing done. Here is in below video I used picture as progressbar. When using images make sure you correctly provide complete locations in the script.

Download this complete project here, it is also available on github.com.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#Load Libraries
Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing 
 
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ApartmentState = "STA"
$Runspace.ThreadOptions = "ReuseThread"
$Runspace.Open()
 
$code = {
#Build the GUI
[xml]$xaml = @"
<Window 
        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:WpfApp6"
        Title="ProgressBar Demo" Height="220" Width="500" Topmost="True">
    <Grid>
        <ProgressBar Name="ProgressBar1" HorizontalAlignment="Left" Height="41" VerticalAlignment="Top" Width="264" Margin="10,10,0,0" Background="{x:Null}">
            <ProgressBar.OpacityMask>
                <ImageBrush ImageSource="C:\Temp\Processbar\Images\Processing.png"/>
            </ProgressBar.OpacityMask>
        </ProgressBar>
        <TextBlock Name="TextBlock1" HorizontalAlignment="Left" Margin="281,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
        <Button Name="Button1" Content="Complete" HorizontalAlignment="Left" Margin="279,31,0,0" VerticalAlignment="Top" Width="75"/>
        <ProgressBar Name="ProgressBar2" HorizontalAlignment="Left" Height="41" VerticalAlignment="Top" Width="264" Margin="10,56,0,0">
            <ProgressBar.OpacityMask>
                <ImageBrush ImageSource="C:\Temp\Processbar\Images\Completed.png"/>
            </ProgressBar.OpacityMask>
        </ProgressBar>
        <TextBlock Name="TextBlock2" HorizontalAlignment="Left" Margin="279,56,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="128"/>
        <Button Name="Button2" Content="Complete" HorizontalAlignment="Left" Margin="279,72,0,0" VerticalAlignment="Top" Width="75"/>
        <ProgressBar Name="ProgressBar3" HorizontalAlignment="Left" Height="41" VerticalAlignment="Top" Width="203" Margin="10,102,0,0" >
            <ProgressBar.OpacityMask>
                <ImageBrush ImageSource="C:\Temp\Processbar\Images\Loading.png"/>
            </ProgressBar.OpacityMask>
        </ProgressBar>
        <TextBlock Name="TextBlock3" HorizontalAlignment="Left" Margin="218,102,0,0" TextWrapping="Wrap" Text="Loading" VerticalAlignment="Top" Width="189"/>
        <Button Name="Button3" Content="Load" HorizontalAlignment="Left" Margin="218,123,0,0" VerticalAlignment="Top" Width="75"/>
    </Grid>
</Window>
"@
 
    $syncHash = [hashtable]::Synchronized(@{})
    $reader=(New-Object System.Xml.XmlNodeReader $xaml)
    $syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )
 
    function Update-Progress {
        param($syncHash,$ProgressNumber,$TargetBox)
        $syncHash.Host = $host
        $Runspace = [runspacefactory]::CreateRunspace()
        $Runspace.ApartmentState = "STA"
        $Runspace.ThreadOptions = "ReuseThread"
        $Runspace.Open()
        $Runspace.SessionStateProxy.SetVariable("syncHash",$syncHash) 

        $Runspace.SessionStateProxy.SetVariable("ProgressNumber",$ProgressNumber)
        #$Runspace.SessionStateProxy.SetVariable("TargetBox",$TargetBox)
 
        $code = {
            $syncHash.Window.Dispatcher.invoke(
                [action]{ 
                    $syncHash.Button1.isEnabled = $false
                    $syncHash.Button2.isEnabled = $false
                    $syncHash.Button3.isEnabled = $false
                }
            )
            if ($ProgressNumber -eq 1) {
                for ($i=0; $i -lt 101; $i++) {
                    $syncHash.Window.Dispatcher.invoke(
                        [action]{ 
                            $syncHash.ProgressBar1.Value = $i
                            $syncHash.TextBlock1.Text = "Working On Comp$i"
                        }
                    )
                    Start-Sleep -Milliseconds 100
                }
            }
            elseif ($ProgressNumber -eq 2) {
                for ($i=0; $i -lt 101; $i++) {
                    $syncHash.Window.Dispatcher.invoke(
                        [action]{ 
                            $syncHash.ProgressBar2.Value = $i
                            $syncHash.TextBlock2.Text = "Process created $i"
                        }
                    )
                    Start-Sleep -Milliseconds 100
                }
            }
            elseif ($ProgressNumber -eq 3) {
                for ($i=0; $i -lt 101; $i++) {
                    $syncHash.Window.Dispatcher.invoke(
                        [action]{ 
                            $syncHash.ProgressBar3.IsIndeterminate = $True
                            $syncHash.TextBlock3.Text = "Loading... File$i"
                        }
                    )
                    Start-Sleep -Milliseconds 100
                }
            }

            $syncHash.Window.Dispatcher.invoke(
                [action]{ 
                    $syncHash.Button1.isEnabled = $true
                    $syncHash.Button2.isEnabled = $true
                    $syncHash.Button3.isEnabled = $true
                    $syncHash.ProgressBar3.IsIndeterminate = $false
                }
            )        
        }
        $PSinstance = [powershell]::Create().AddScript($Code)
        $PSinstance.Runspace = $Runspace
        $job = $PSinstance.BeginInvoke()
    }

    #AutoFind all controls
    $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]")  | ForEach-Object { 
        #New-Variable  -Name $_.Name -Value $Form.FindName($_.Name) -Force
        $syncHash.Add($_.Name, $syncHash.Window.Findname($_.Name))
    }

    # Click Actions
    $syncHash.Button1.Add_Click({
        Update-Progress -syncHash $syncHash -ProgressNumber 1
    })

    $syncHash.Button2.Add_Click({
        Update-Progress -syncHash $syncHash -ProgressNumber 2
    })

    $syncHash.Button3.Add_Click({
        Update-Progress -syncHash $syncHash -ProgressNumber 3
    })
 
    $syncHash.Window.ShowDialog()
    $Runspace.Close()
    $Runspace.Dispose()
}
 
$PSinstance1 = [powershell]::Create().AddScript($Code)
$PSinstance1.Runspace = $Runspace
$job = $PSinstance1.BeginInvoke()

Useful Articles
Part 1: Create WPF XAML powershell GUI form with Visual studio
Part 2: Powershell and WPF: Build GUI applications tutorial
Part 3: Create shorter Microsoft Powershell WPF automated clean script
Powershell PoshGUI: Convert user to SID and vice versa using
Microsoft Powershell GUI: Change Internet Options connections Lan settings proxy server grayed out

Go Back

Quick question now that we have created items in their own thread, how can I do an invoke-command or start-process to open a folder or .exe. When I run this now I never see this since it's on a background thread.

Reply


Comment

Blog Search

Page Views

1970506

Follow me on Blogarama