PoshProgressBar.psm1
#.ExternalHelp PoshProgressBar.psm1-help.xml Function New-ProgressBar { param( [Parameter(ParameterSetName = 'Standard')] [Parameter(ParameterSetName = 'MaterialDesign')] [String]$IconPath, [Parameter(ParameterSetName = 'Standard')] [Parameter(ParameterSetName = 'MaterialDesign')] [Bool]$IsIndeterminate = $True, [Parameter(ParameterSetName = 'MaterialDesign', Mandatory = $true)] [switch]$MaterialDesign, [Parameter(ParameterSetName = 'MaterialDesign')] [ValidateSet("Circle", "Horizontal", "Vertical")] [String]$Type = "Horizontal", [Parameter(ParameterSetName = 'MaterialDesign')] [ValidateSet("Red", "Pink", "Purple", "DeepPurple", "Indigo", "Blue", "LightBlue", "Cyan", "Teal", "Green", "LightGreen", "Lime", "Yellow", "Amber", "Orange", "DeepOrange", "Brown", "Grey", "BlueGrey")] [String]$PrimaryColor = "Blue", [Parameter(ParameterSetName = 'MaterialDesign')] [ValidateSet("Red", "Pink", "Purple", "DeepPurple", "Indigo", "Blue", "LightBlue", "Cyan", "Teal", "Green", "LightGreen", "Lime", "Yellow", "Amber", "Orange", "DeepOrange")] [String]$AccentColor = "LightBlue", [Parameter(ParameterSetName = 'Standard')] [Parameter(ParameterSetName = 'MaterialDesign')] [ValidateSet("Large", "Medium", "Small")] [String]$Size = "Medium", [Parameter(ParameterSetName = 'MaterialDesign')] [ValidateSet("Dark", "Light")] [String]$Theme = "Light" ) $ProgressSize = @{"Small" = 140; "Medium" = 280; "Large" = 560 } [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null [System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | Out-Null $syncHash = [hashtable]::Synchronized(@{ }) $newRunspace = [runspacefactory]::CreateRunspace() $syncHash.Runspace = $newRunspace $syncHash.Closing = $False $syncHash.SecondsRemainingInput = $Null $syncHash.StatusInput = '' $syncHash.CurrentOperationInput = '' $syncHash.IconPath = $IconPath $syncHash.XAML = @" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="Window" Title="Progress..." WindowStartupLocation = "CenterScreen" Width = "$($ProgressSize[$Size]+75)" SizeToContent = "Height" ShowInTaskbar = "True" $(if($MaterialDesign){ @' TextElement.Foreground="{DynamicResource MaterialDesignBody}" Background="{DynamicResource MaterialDesignPaper}" TextElement.FontWeight="Medium" TextElement.FontSize="14" FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto" '@ }) $( if($SyncHash.IconPath){ @" Icon="$($SyncHash.IconPath)" "@ } ) > $( if($MaterialDesign) { @" <Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.$Theme.xaml" /> <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" /> <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.$PrimaryColor.xaml" /> <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.$AccentColor.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> "@ } ) <StackPanel Margin="20"> $( if($MaterialDesign) { @" <ProgressBar $( switch($Type) { "Circle" { @" Style="{StaticResource MaterialDesignCircularProgressBar}" Height="$($ProgressSize[$Size]+10)" Width="$($ProgressSize[$Size])" "@ } "Horizontal" { @" Orientation="Horizontal" Width="$($ProgressSize[$Size])" "@ } "Vertical" { @" Orientation="Vertical" Height="$($ProgressSize[$Size])" "@ } } ) IsIndeterminate="$($IsIndeterminate)" Name="ProgressBar" /> "@ } else { @" <ProgressBar IsIndeterminate="$($IsIndeterminate)" Width="$($ProgressSize[$Size])" Name="ProgressBar" /> "@ } ) <TextBlock Name="PercentCompleteTextBlock" Visibility="Hidden" StackPanel.ZIndex = "99" Text="{Binding ElementName=ProgressBar, Path=Value, StringFormat={}{0:0}%}" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Name="Status" Text="" HorizontalAlignment="Left" /> <TextBlock Name="TimeRemaining" Text="" HorizontalAlignment="Left" /> <TextBlock Name="CurrentOperation" Text="" HorizontalAlignment="Left" /> </StackPanel> </Window> "@ $newRunspace.ApartmentState = "STA" $newRunspace.ThreadOptions = "ReuseThread" $data = $newRunspace.Open() | Out-Null $newRunspace.SessionStateProxy.SetVariable("syncHash", $syncHash) $PowerShellCommand = [PowerShell]::Create().AddScript( { $syncHash.Window = [Windows.Markup.XamlReader]::parse( $SyncHash.XAML ) #=========================================================================== # Store Form Objects In PowerShell #=========================================================================== ([xml]$SyncHash.XAML).SelectNodes("//*[@Name]") | % { $SyncHash."$($_.Name)" = $SyncHash.Window.FindName($_.Name) } $TimeRemaining = [System.TimeSpan] $updateBlock = { if ($SyncHash.ProgressBar.IsIndeterminate) { $SyncHash.PercentCompleteTextBlock.Visibility = [System.Windows.Visibility]::Hidden } else { $SyncHash.PercentCompleteTextBlock.Visibility = [System.Windows.Visibility]::Visible } if ($SyncHash.Closing -eq $True) { $SyncHash.NotifyIcon.Visible = $false $syncHash.Window.Close() [System.Windows.Forms.Application]::Exit() Break } $SyncHash.Window.Title = $SyncHash.Activity $SyncHash.ProgressBar.Value = $SyncHash.PercentComplete if ([string]::IsNullOrEmpty($SyncHash.PercentComplete) -ne $True -and $SyncHash.ProgressBar.IsIndeterminate -eq $True) { $SyncHash.ProgressBar.IsIndeterminate = $False } $SyncHash.Status.Text = $SyncHash.StatusInput if ($SyncHash.SecondsRemainingInput) { $TimeRemaining = [System.TimeSpan]::FromSeconds($SyncHash.SecondsRemainingInput) $SyncHash.TimeRemaining.Text = '{0:00}:{1:00}:{2:00}' -f $TimeRemaining.Hours, $TimeRemaining.Minutes, $TimeRemaining.Seconds } $SyncHash.CurrentOperation.Text = $SyncHash.CurrentOperationInput $SyncHash.NotifyIcon.text = "Activity: $($SyncHash.Activity)`nPercent Complete: $($SyncHash.PercentComplete)" } ############### New Blog ############## $syncHash.Window.Add_SourceInitialized( { ## Before the window's even displayed ... ## We'll create a timer $timer = new-object System.Windows.Threading.DispatcherTimer ## Which will fire 4 times every second $timer.Interval = [TimeSpan]"0:0:0.01" ## And will invoke the $updateBlock $timer.Add_Tick( $updateBlock ) ## Now start the timer running $timer.Start() if ( $timer.IsEnabled ) { } else { $clock.Close() Write-Error "Timer didn't start" } } ) # Extract icon from PowerShell to use as the NotifyIcon if ($syncHash.IconPath) { $icon = [System.Drawing.Icon]::new($syncHash.IconPath) $syncHash.Window.Icon = $Icon } else { $icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$pshome\powershell.exe") } # Create notifyicon, and right-click -> Exit menu $SyncHash.NotifyIcon = New-Object System.Windows.Forms.NotifyIcon $SyncHash.NotifyIcon.Text = "Activity: $($SyncHash.Activity)`nPercent Complete: $($SyncHash.PercentComplete)" $SyncHash.NotifyIcon.Icon = $icon $SyncHash.NotifyIcon.Visible = $true $menuitem = New-Object System.Windows.Forms.MenuItem $menuitem.Text = "Exit" $contextmenu = New-Object System.Windows.Forms.ContextMenu $SyncHash.NotifyIcon.ContextMenu = $contextmenu $SyncHash.NotifyIcon.contextMenu.MenuItems.AddRange($menuitem) $SyncHash.NotifyIcon.add_DoubleClick( { $synchash.window.Show() }) # When Exit is clicked, close everything and kill the PowerShell process $menuitem.add_Click( { $SyncHash.NotifyIcon.Visible = $false $syncHash.Closing = $True $syncHash.Window.Close() [System.Windows.Forms.Application]::Exit() }) $Synchash.window.Add_Closing( { if ($SyncHash.Closing -eq $True) { } else { $SyncHash.Window.Hide() $SyncHash.NotifyIcon.BalloonTipTitle = "Your script is still running..." $SyncHash.NotifyIcon.BalloonTipText = "Double click to open the progress bar again." $SyncHash.NotifyIcon.ShowBalloonTip(100) $_.Cancel = $true } }) $syncHash.Window.Show() | Out-Null $appContext = [System.Windows.Forms.ApplicationContext]::new() [void][System.Windows.Forms.Application]::Run($appContext) $syncHash.Error = $Error }) $PowerShellCommand.Runspace = $newRunspace $data = $PowerShellCommand.BeginInvoke() Register-ObjectEvent -InputObject $SyncHash.Runspace ` -EventName 'AvailabilityChanged' ` -Action { if ($Sender.RunspaceAvailability -eq "Available") { $Sender.Closeasync() $Sender.Dispose() } } | Out-Null return $syncHash } #.ExternalHelp PoshProgressBar.psm1-help.xml function Write-ProgressBar { Param ( [Parameter(Mandatory = $true)] $ProgressBar, [Parameter(Mandatory = $true)] [String]$Activity, [int]$PercentComplete, [String]$Status = $Null, [int]$SecondsRemaining = $Null, [String]$CurrentOperation = $Null ) if ($ProgressBar.Closing -eq $true) { exit } Write-Verbose -Message "Setting activity to $Activity" $ProgressBar.Activity = $Activity if ($PercentComplete) { $ProgressBar.PercentComplete = $PercentComplete } $ProgressBar.SecondsRemainingInput = $SecondsRemaining $ProgressBar.StatusInput = $Status $ProgressBar.CurrentOperationInput = $CurrentOperation } #.ExternalHelp PoshProgressBar.psm1-help.xml function Close-ProgressBar { Param ( [Parameter(Mandatory = $true)] $ProgressBar ) $ProgressBar.Closing = $True } |