CommonControls/Show-Clock.ps1
function Show-Clock { <# .Synopsis Shows a clock. .Description Shows a clock, or counts down to a time and displays a message .Example Show-Clock -AsJob .Example $now = Get-Date $christmas = $now.Subtract($now.TimeOfDay).AddDays(-$now.dayofyear).AddMonths(12).AddDays(25) Show-Clock -Foreground DarkGreen -CountDownTo $newYears -CompletedMessage "Merry Christmas" -TimeFormat "{0:dd} Days & {0:\:hh\:mm\:ss} Until Christmas" -FullScreen -AsJob .Example # A clock within a control New-StackPanel { New-Label -HorizontalAlignment Center "The Time Is Now" Show-Clock -FontSize 12 -FontFamily Tahoma } -show #> [CmdletBinding(DefaultParameterSetName='Clock')] param( # Counts down to a point in time [Parameter(Mandatory=$true,ParameterSetName='CountDown')] [DateTime] $CountDownTo, # The message to show when the countdown completes [Parameter(ParameterSetName='CountDown')] [string] $CompletedMessage, # The format string used for the DateTime, or TimeSpan. # By default, this is "F" for the Clock parameter set and # g for the countdown [string] $TimeFormat, # The foreground brush. By default, the foreground will be black. $Foreground, # The background brush. By default, the background will be transparent $Background, # The font family (font name). By default, the font family will be 'Impact' $FontFamily, # The Font Size [Double]$FontSize, # The Font Weight $FontWeight, # The Font Style. $FontStyle, [Switch]$FullScreen, # The name of the control [string]$Name, # If the control is a child element of a Grid control (see New-Grid), # then the Row parameter will be used to determine where to place the # top of the control. Using the -Row parameter changes the # dependency property [Windows.Controls.Grid]::RowProperty [Int]$Row, # If the control is a child element of a Grid control (see New-Grid) # then the Column parameter will be used to determine where to place # the left of the control. Using the -Column parameter changes the # dependency property [Windows.Controls.Grid]::ColumnProperty [Int]$Column, # If the control is a child element of a Grid control (see New-Grid) # then the RowSpan parameter will be used to determine how many rows # in the grid the control will occupy. Using the -RowSpan parameter # changes the dependency property [Windows.Controls.Grid]::RowSpanProperty [Int]$RowSpan, # If the control is a child element of a Grid control (see New-Grid) # then the RowSpan parameter will be used to determine how many columns # in the grid the control will occupy. Using the -ColumnSpan parameter # changes the dependency property [Windows.Controls.Grid]::ColumnSpanProperty [Int]$ColumnSpan, # The -Width parameter will be used to set the width of the control [Int]$Width, # The -Height parameter will be used to set the height of the control [Int]$Height, # If the control is a child element of a Canvas control (see New-Canvas), # then the Top parameter controls the top location within that canvas # Using the -Top parameter changes the dependency property # [Windows.Controls.Canvas]::TopProperty [Double]$Top, # If the control is a child element of a Canvas control (see New-Canvas), # then the Left parameter controls the left location within that canvas # Using the -Left parameter changes the dependency property # [Windows.Controls.Canvas]::LeftProperty [Double]$Left, # If the control is a child element of a Dock control (see New-Dock), # then the Dock parameter controls the dock style within that panel # Using the -Dock parameter changes the dependency property # [Windows.Controls.DockPanel]::DockProperty [Windows.Controls.Dock]$Dock, # If Show is set, then the UI will be displayed as a modal dialog within the current # thread. If the -Show and -AsJob parameters are omitted, then the control should be # output from the function [Switch]$Show, # If AsJob is set, then the UI will displayed within a WPF job. [Switch]$AsJob ) process { # The one parameter that is used for both the outer control and the inner # command is the background. Add a default value before anything else happens if (-not $psBoundParameters.Background) { $psBoundParameters.Background = 'Transparent' } # First, copy off the UI parameters, so the border doesn't have problems # with parameters that it can't deal with. $uiParameters = @{} + $psBoundParameters $innerParameters = 'TimeFormat', 'CountDownTo', 'CompletedMessage', 'FontSize', 'FontFamily', 'FontWeight', 'FontStyle', 'Foreground', 'Fullscreen' foreach ($innerParameter in $innerParameters) { $null = $uiParameters.Remove($innerParameter) } # If there was no timeformat, set the timeformat to a good default. # "F" is the full localized date time format # "g" is the short localized timespan format. if (-not $timeFormat) { if (-not $countDownTo) { $TimeFormat = "F" } else { $TimeFormat = "hh\:mm\:ss" } } if ($timeFormat -notlike "{0:*") { $TimeFormat = "{0:$TimeFormat}" } $psBoundParameters.TimeFormat = $TimeFormat if (-not $psBoundParameters.CompletedMessage) { $psBoundParameters.CompletedMessage = "Done!" } if (-not $psBoundParameters.FontSize) { $psBoundParameters.FontSize = 32 } if (-not $psBoundParameters.FontFamily) { $psBoundParameters.FontFamily = 'Impact' } if (-not $psBoundParameters.FontStyle) { $psBoundParameters.FontStyle = "Normal" } if (-not $psBoundParameters.FontWeight) { $psBoundParameters.FontWeight = "Normal" } if (-not $psBoundParameters.Foreground) { $psBoundParameters.ForeGround = 'Black' } $psBoundParameters.FullScreen =$fullScreen New-Border @uiParameters -HorizontalAlignment Stretch -VerticalAlignment Stretch -On_Initialized { # Initialized happens right after the control has been created, but before it has been displayed. # In this, we change the window settings if this resides directly inside of the window. # This lets the clock be both cool, and practical if ($this.Parent -is [Windows.Window]) { if (-not $FullScreen) { $window.SizeToContent = 'WidthAndHeight' # If the background is transparent, make the window transparent as well. if ($background -eq 'Transparent') { $window.WindowStyle = 'None' $window.Background = 'Transparent' $window.AllowsTransparency =$true } else { $this.CornerRadius = 20 $this.BorderThickness = 2 $this.BorderBrush = 'Black' } } else { $window.WindowStyle = 'None' $window.WindowState = 'Maximized' $window.HorizontalContentAlignment = 'center' $window.VerticalContentAlignment = 'center' $window.SizeToContent = 'Manual' } $window.WindowStartupLocation = 'CenterScreen' # When the window is closing, stop the clock Add-EventHandler -EventName "On_Closing" -Handler { if ($this.Content.DataContext.Command.Stop) { $this.Content.DataContext.Command.Stop() } } -Object $window # When the right mouse button is down, close the control Add-EventHandler -EventName "On_PreviewMouseRightButtonDown" -Handler { $_.Handled = $true Close-Control } -Object $window if (-not $FullScreen) { # When the left mouse button is down, drag the window. Add-EventHandler -EventName "On_PreviewMouseLeftButtonDown" -Handler { $_.Handled = $true $this.DragMove() } -Object $window } } } -On_Loaded { # When the control is loaded, process the parameters. Variables will automatically # be created to help you work input from the parent function # First, set the background, and create a label using the parameters that were passed on in $this.Background = $Background # Now, go ahead and set the borders' child to be the $this.Child = New-Label -HorizontalContentAlignment Center -VerticalContentAlignment Center -Foreground $foreground -FontSize $FontSize -FontStyle $FontStyle -FontWeight $FontWeight -FontFamily $FontFamily # We have two modes of using the control: CountDown and Clock if (-not $CountDownTo) { # Clock is really easy. Just create a background data source # that outputs the time, and change the output accordingly. $this.DataContext = Get-PowerShellDataSource -On_OutputChanged { $output = Get-PowerShellOutput -Last -OutputOnly $This.Child.Content = [String]::Format($timeFormat, $output) } -Script { while ($true) { Get-Date; Start-Sleep -Seconds 1 } } } else { # Countdown is a little trickier. We use [ScriptBlock]::Create() # to embed the countdown's value inside of the countdown code $sb = [ScriptBLock]::Create(" `$countdownTo = [DateTime]'$countDownTo' " + { do { $timeLeft = $countDownTo - (Get-Date) if ($timeLeft.TotalMilliseconds -le 0) { "Completed" } else { $timeLeft } Start-Sleep -Seconds 1 } while ($timeLeft.TotalMilliseconds -gt 0) } ) # The background data sources uses that script. When the output changes, # if the output was a timespan, update the label with the value. Otherwise, # set the labels' content to the completed message $this.DataContext = Get-PowerShellDataSource -On_OutputChanged { $output = Get-PowerShellOutput -Last -OutputOnly if ($output -is [TimeSpan]) { $This.Child.Content = [String]::Format($timeFormat, $Output) } else { $this.Child.Content = $CompletedMessage } } -Script $sb } } } } |