TimeCalculator.ps1

<#PSScriptInfo
 
.VERSION 2.5
 
.GUID 9f55c5d0-0031-4145-ba28-aae07df5cb61
 
.AUTHOR Chris Carter
 
.COMPANYNAME
 
.COPYRIGHT 2016 Chris Carter
 
.TAGS AddTime SubstractTime CalculateTime AverageTime
 
.LICENSEURI http://creativecommons.org/licenses/by-sa/4.0/
 
.PROJECTURI https://gallery.technet.microsoft.com/Time-Calculator-20-GUI-c30dfa78
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


<#
.SYNOPSIS
Adds or subtracts time spans entered in the format 'hours:minutes:seconds'
.DESCRIPTION
Time Calculator adds or subtracts time spans entered in the format of 'hours:minutes:seconds' or 'hours.minutes.seconds.'
 
By default, Time Calculator will produce output as '1 hr 25 mins 35 seconds. Toggling Traditional Format in the View menu will cause output to be converted to 01:25:35.
 
Time Calculator shows output in hours, minutes, and seconds. Toggling Show Days in the View menu will cause output to be changed to days, hours, minutes, and seconds.
 
Selecting Calculate Average from the View Menu will cause the entered times to be calculated as an average.
 
.LINK
New-TimeSpan
#>


#Requires -Version 3.0
Add-Type -AssemblyName PresentationCore,PresentationFrameWork,WindowsBase

Function sc-Or ($ReturnIfTrue,$OrReturn) {
    if ($ReturnIfTrue) {$ReturnIfTrue}
    else {$OrReturn}
}

Function Toggle-CheckItem ($InputObject) {
    if ($InputObject.IsChecked) {$InputObject.IsChecked = $false}
    else {$InputObject.IsChecked = $true}
}

Function Add-CommandAndKeyBinding ($CanExec,$Exec,$Key,$KeyModifiers,$KeyDesc,$CommandName,$CommandDesc,$CommandBindTarget,$KeyBindTarget) {
    #Build KeyGesture for Command
    $KeyGesture = New-Object System.Windows.Input.KeyGesture -ArgumentList `
        $Key, $KeyModifiers, $KeyDesc

    $GestColl = New-Object System.Windows.Input.InputGestureCollection
    $GestColl.Add($KeyGesture) | Out-Null

    #Create Routed Command
    $Command = New-Object System.Windows.Input.RoutedUICommand -ArgumentList $CommandDesc,$CommandName,([Type]"System.Object"),$GestColl

    #Create Command Binding
    $CommandBind = New-Object System.Windows.Input.CommandBinding -ArgumentList $Command,$Exec,$CanExec

    #Add to target
    $CommandBindTarget.CommandBindings.Add($CommandBind) | Out-Null

    #Add Key Binding to target
    if ($KeyBindTarget) {
        $KeyBindTarget.Command = $Command
    }
}

Function Format-FriendlyOutput {
    Param(
        [Int]$Value,
        [ValidateSet("day","hr","min","sec")]
            [String]$Unit
    )
    if ($Value -ne 0) {
        if ($Value -gt 1) {$addEss = 's'}
        else {''}

        "$Value $Unit$addEss "
    }
    else {''}
}

Function Format-Output ($Object) {
    if ($MenuFormat.IsChecked) {
        $days = "{0:d2}" -f $Object.Days
        $hrs = "{0:d2}" -f $Object.Hours
        $totalHrs = "{0:d2}" -f [int]([Math]::Floor($Object.TotalHours))
        $mins = "{0:d2}" -f $Object.Minutes
        $secs = "{0:d2}" -f $Object.Seconds
        if ($MenuDays.IsChecked) {
            $output = "$days`:$hrs`:$mins`:$secs"
        }
        else {$output = "$totalHrs`:$mins`:$secs"}
    }
    else {
        $days = Format-FriendlyOutput -Value $Object.Days -Unit day
        $hrs = Format-FriendlyOutput -Value $Object.Hours -Unit hr
        $totalHrs = Format-FriendlyOutput -Value ([Math]::Floor($Object.TotalHours)) -Unit hr
        $mins = Format-FriendlyOutput -Value $Object.Minutes -Unit min
        $secs = Format-FriendlyOutput -Value $Object.Seconds -Unit sec
        if ($MenuDays.IsChecked) {
            $output = "$days$hrs$mins$secs"
        }
        else {$output = "$totalHrs$mins$secs"} 
    }
    #Remove trailing space if it exists
    if ($output[-1] -eq ' ') {
        $output.Substring(0,$output.Length-1)
    }
    else {$output}
}

Function Do-TimeMath {
    $nextOp = ""
    [TimeSpan]$total = [TimeSpan]::Zero
    foreach ($op in $collOps) {
        if (!$nextOp -or $nextOp -eq "+" -or $MenuAvg.IsChecked) {
            try {$total += $op} 
            catch {}
        }
        elseif ($nextOp -eq "-") {
            try {$total -= $op}
            catch {}
        }
        $nextOp = $op.FollowingOperator
    }

    if ($MenuAvg.IsChecked) {
        $tickTot = $total.Ticks
        $avgTicks = $tickTot/$collOps.Count
        $total = New-Object System.TimeSpan -ArgumentList $avgTicks
    }
    $Global:calcResult = $total
    $total
}

Function Perform-Calculation {
    $result = Do-TimeMath
    $InOutBox.IsTextFromCalculation = $true
    $InOutBox.Text = Format-Output $result
}    

Function New-TimeSpanCustomObject ($InputObject, $Operator) {
    if ($InputObject -isnot [TimeSpan]) {
        $timeSpan = New-TimeSpan -Hours  (sc-Or $InputObject.Hours 0) -Minutes (sc-Or $InputObject.Minutes 0) -Seconds (sc-Or $InputObject.Seconds 0)
    }
    else {$timeSpan = $InputObject}

    if ($Operator) {
        $timeSpan | Add-Member -MemberType NoteProperty -Name FollowingOperator -Value $Operator
    }
    $timeSpan
}

Function Print-OperandList {
    $OpBlock.Text = ''
    foreach ($op in $collOps) {
        $OpBlock.Text += (Format-Output -Object $op) + " $($op.FollowingOperator) "
    }   
}

Function Add-EntryToCalculation ($InputObject, $Operator) {
    try{
    if ($InOutBox.Text -match $regxpTime) {
        $timeSpan = New-TimeSpanCustomObject -InputObject $Matches -Operator $Operator
    }
    elseif ($InputObject) {
        $timeSpan = New-TimeSpanCustomObject -InputObject $InputObject -Operator $Operator
    }
    
    $collOps.Add($timeSpan)

    Perform-Calculation
    Print-OperandList
    }
    catch  {
        $InOutBox.Text = "Err: Time is too long"
    }
}

Function Add-InputToScreen {
    if (($InOutBox.Text.Length -lt 27 -and !$InOutBox.IsTextFromCalculation) -or ($InOutBox.Text.Length -lt 65 -and $InOutBox.IsTextFromCalculation)) {
        if ($InOutBox.Text -eq '0' -or $InOutBox.IsTextFromCalculation) {
            $InOutBox.IsTextFromCalculation = $false
            $InOutBox.Text = $Matches.CorrectEntry
        }
        else {$InOutBox.Text += $Matches.CorrectEntry}
    }
}

Function Remove-LastCharacter {
    if (!$InOutBox.IsTextFromCalculation) {
        $InOutBox.Text = $InOutBox.Text.Substring(0,$InOutBox.Text.Length-1)
    }
}

Function Remove-CurrentEntry {
    $InOutBox.Text = ''
}

Function Clear-All {
    $InOutBox.Text, $OpBlock.Text = ''
    $InOutBox.IsTextFromCalculation = $false
    $collOps.Clear()
    $calcResult = [timespan]::Zero
}

Function Restart-Calculation {
    if ($calcResult.Ticks) {
        Add-EntryToCalculation -InputObject $calcResult
    }
    else {Add-EntryToCalculation}
    $collOps.Clear()
    $OpBlock.Text = ''    
}

#Event Handlers
#Main Window Event Handlers

Function On-TextInput {
    if ($_.Text -match '(?<CorrectEntry>[\d]|\.|:)') {
        Add-InputToScreen
    }
    elseif ($_.Text -match '[\b]') {
        Remove-LastCharacter
    }
    elseif ($_.Text -match '\+' -or $_.Text -match '\-') {
        if ($InOutBox.Text -ne '0' -and !$InOutBox.IsTextFromCalculation) {
            Add-EntryToCalculation -Operator $_.Text
        }
        if ($collOps.Count -eq 0 -and $InOutBox.IsTextFromCalculation) {
            Add-EntryToCalculation -InputObject $calcResult -Operator $_.Text
        }
    }
}

Function On-KeyUp {
    if ($_.Key -eq "Delete") {
        Remove-CurrentEntry
    }
    elseif ($_.Key -eq "Return") {
        Restart-Calculation
    }
}

Function On-InOutTextChanged {
    if ($this.Text.Length -gt 30) {
        $InOutBlock.FontSize = 11
    }
    elseif ($this.Text.Length -gt 20) {
        $InOutBlock.FontSize = 12
    }
    else {$InOutBlock.FontSize = 16}

    if ($this.Text -eq '') {
        $this.Text = '0'
    }

    $InOutBlock.Text = $this.Text
    if ($this.Text -notmatch $regxpTime -and !$InOutBox.IsTextFromCalculation) {
        
        $InOutBlock.Foreground = "Red"
        $InOutBlock.ToolTip = $WrongToolTip
    }
    else {
        $InOutBlock.Foreground = "Black"
        $InOutBlock.ToolTip = $null
    }

    $_.Handled = $true
}

Function On-MenuFormatClick {
    if ($collOps.Count -ne 0) {
        Perform-Calculation
        Print-OperandList
    }
    else {Restart-Calculation}

    $_.Handled = $true
}

Function On-MenuDaysClick {
    if ($collOps.Count -ne 0) {
        Perform-Calculation
        Print-OperandList
    }
    else {Restart-Calculation}

    $_.Handled = $true
}

Function On-MenuAvgClick {
    if ($collOps.Count -ne 0) {
        Perform-Calculation
        Print-OperandList
    }
    else {Restart-Calculation}

    $_.Handled = $true
}

Function On-EqualClick {
    $key = [System.Windows.Input.Key]::Return
    $target = $Window
    $routedEvent = [System.Windows.Input.Keyboard]::KeyUpEvent
    $KeyEventArgs = New-Object System.Windows.Input.KeyEventArgs -ArgumentList `
        ([System.Windows.Input.Keyboard]::PrimaryDevice, [System.Windows.PresentationSource]::FromVisual($target), 0, $key)

    $KeyEventArgs.RoutedEvent = $routedEvent

    $Window.RaiseEvent($KeyEventArgs)

    $_.Handled = $true
}

Function On-NumPadClick {
    $text = $this.Content
    $routedEvent = [System.Windows.Input.TextCompositionManager]::TextInputEvent
    $TextComp = New-Object System.Windows.Input.TextComposition -ArgumentList `
        ([System.Windows.Input.InputManager]::Current, $Window, $text)

    $TextCompEventArgs = New-Object System.Windows.Input.TextCompositionEventArgs -ArgumentList `
        ([System.Windows.Input.InputManager]::Current.PrimaryKeyboardDevice, $TextComp)

    $TextCompEventArgs.RoutedEvent = $routedEvent

    $Window.RaiseEvent($TextCompEventArgs)

    $_.Handled = $true
}

#About Window Event Handlers
Function On-ProfileRequestNavigate {
    [System.Diagnostics.Process]::Start($_.Uri.ToString())
}

#Modal Dialog Creation Functions

Function New-AboutDialogBox {
    #Load About Window
    $aboutXmlReader = New-Object System.Xml.XmlNodeReader -ArgumentList $aboutXaml
    $AboutWindow = [System.Windows.Markup.XamlReader]::Load($aboutXmlReader)
    $AboutWindow.Owner = $this
    $AboutWindow.WindowStyle = "ToolWindow"

    #Set Big Icon for Presentation
    #Remove Title Bar Icon
    $BigIcon = $AboutWindow.FindName("AboutIcon")
    $HeaderTitle = $AboutWindow.FindName("AboutHeaderTitle")

    if ($icon) {
        $BigIcon.Source = $icon
    }
    else {
        $BigIcon.Visibility = "Collapsed"
        $HeaderTitle.Margin = New-Object System.Windows.Thickness -ArgumentList 25,20,0,20
    }

    #About Window Controls
    $ProfileLink = $AboutWindow.FindName("ProfileLink")

    #About Window Evnts
    $ProfileLink.add_RequestNavigate({On-ProfileRequestNavigate})

    $AboutWindow.ShowDialog()
}

Function New-HelpDialogBox {
    #Load Help Window
    $helpXmlReader = New-Object System.Xml.XmlNodeReader -ArgumentList $helpXaml
    $HelpWindow = [System.Windows.Markup.XamlReader]::Load($helpXmlReader)
    $HelpWindow.Owner = $this

    $BigIcon = $HelpWindow.FindName("HelpHeaderIcon")
    $HeaderTitle = $HelpWindow.FindName("HelpHeaderTitle")

    if ($icon) {
        $BigIcon.Source = $icon
        $HelpWindow.Icon = $icon
    }
    else {
        $BigIcon.Visibility = "Collapsed"
        $HeaderTitle.Margin = New-Object System.Windows.Thickness -ArgumentList 25,25,0,15
    }

    $HelpWindow.ShowDialog()
}

#Icon File
#$icon = New-Object System.Windows.Media.Imaging.BitmapImage -ArgumentList ''

#XAML Code
#Main Window XAML
[Xml]$xaml = @'
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Time Calculator" ResizeMode="NoResize" SizeToContent="WidthAndHeight">
     
    <Window.Resources>
        <ToolTip x:Key="WrongFormatToolTip">
            <StackPanel>
                <TextBlock FontWeight="Bold">Incorrect Format</TextBlock>
                <TextBlock>Please enter time in the format '<Italic>hours:minutes:seconds</Italic>'</TextBlock>
            </StackPanel>
        </ToolTip>
 
        <ControlTemplate x:Key="{x:Static MenuItem.TopLevelHeaderTemplateKey}" TargetType="MenuItem">
            <Border Name="Border" >
                <Grid>
                    <ContentPresenter Margin="6,3,6,3" ContentSource="Header" RecognizesAccessKey="True" />
                    <Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Fade">
                        <Border Name="SubmenuBorder" SnapsToDevicePixels="True" Background="WhiteSmoke" BorderBrush="WhiteSmoke" BorderThickness="1" >
                            <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
                        </Border>
                    </Popup>
                </Grid>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="IsSuspendingPopupAnimation" Value="true">
                    <Setter TargetName="Popup" Property="PopupAnimation" Value="None"/>
                </Trigger>
                <Trigger Property="IsHighlighted" Value="true">
                    <Setter TargetName="Border" Property="Background" Value="WhiteSmoke"/>
                    <Setter TargetName="Border" Property="BorderBrush" Value="Transparent"/>
                    <Setter Property="Foreground" Value="#cc6023"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Foreground" Value="LightGray"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
 
        <ControlTemplate x:Key="{x:Static MenuItem.SubmenuItemTemplateKey}" TargetType="MenuItem">
            <Border Name="Border" Padding="0,4,0,4">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" SharedSizeGroup="Icon"/>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" SharedSizeGroup="Shortcut"/>
                        <ColumnDefinition Width="13"/>
                    </Grid.ColumnDefinitions>
                    <ContentPresenter Name="Icon" Width="13" Height="13" Margin="6,0,6,0" VerticalAlignment="Center" ContentSource="Icon"/>
                    <Border Name="Check" Width="13" Height="13" Visibility="Collapsed" Margin="6,0,6,0" Background="Transparent" BorderThickness="1" BorderBrush="Transparent">
                        <Path Name="CheckMark" Width="7" Height="7" Visibility="Hidden" SnapsToDevicePixels="False" Stroke="#34495e" StrokeThickness="2" Data="M 0 0 L 7 7 M 0 7 L 7 0" />
                    </Border>
                    <ContentPresenter Name="HeaderHost" Grid.Column="1" ContentSource="Header" RecognizesAccessKey="True"/>
                    <TextBlock x:Name="InputGestureText" Grid.Column="2" Text="{TemplateBinding InputGestureText}" Margin="35,0,0,0" DockPanel.Dock="Right" />
                </Grid>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="Icon" Value="{x:Null}">
                    <Setter TargetName="Icon" Property="Visibility" Value="Hidden"/>
                </Trigger>
                <Trigger Property="IsChecked" Value="true">
                    <Setter TargetName="CheckMark" Property="Visibility" Value="Visible"/>
                </Trigger>
                <Trigger Property="IsCheckable" Value="true">
                    <Setter TargetName="Check" Property="Visibility" Value="Visible"/>
                    <Setter TargetName="Icon" Property="Visibility" Value="Hidden"/>
                </Trigger>
                <Trigger Property="IsHighlighted" Value="true">
                    <Setter TargetName="Border" Property="Background" Value="#cc6023"/>
                    <Setter Property="Foreground" Value="WhiteSmoke" />
                    <Setter TargetName="CheckMark" Property="Stroke" Value="WhiteSmoke"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="false">
                    <Setter Property="Foreground" Value="LightGray"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
 
        <ControlTemplate x:Key="NumPadButtons" TargetType="Button">
            <Border Name="Root" CornerRadius="0" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
                <Border.Effect>
                    <DropShadowEffect Color="Black" Direction="320" ShadowDepth="1" BlurRadius="3" Opacity="0.5"/>
                </Border.Effect>
                <TextBlock Name="ButtonText" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{TemplateBinding Foreground}">
                    <ContentPresenter Margin="0"/>
                </TextBlock>
 
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup Name="CommonStates">
                        <VisualStateGroup.Transitions>
                            <VisualTransition From="MouseOver" To="Normal" GeneratedDuration="0:0:0.5"/>
                            <VisualTransition To="Pressed" GeneratedDuration="0:0:0.15" />
                            <VisualTransition From="Normal" To="MouseOver" GeneratedDuration="0:0:0.25"/>
                            <VisualTransition From="Pressed" To="MouseOver" GeneratedDuration="0:0:0.15"/>
                        </VisualStateGroup.Transitions>
 
                        <VisualState Name="Normal" />
                        <VisualState Name="MouseOver">
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="#cc6023"/>
                                <ColorAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(BorderBrush).(SolidColorBrush.Color)" To="#cc6023"/>
                                <ColorAnimation Storyboard.TargetName="ButtonText" Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)" To="WhiteSmoke"/>
                            </Storyboard>
                        </VisualState>
 
                        <VisualState Name="Pressed">
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="#8F4419"/>
                                <ColorAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(BorderBrush).(SolidColorBrush.Color)" To="#8F4419"/>
                                <ColorAnimation Storyboard.TargetName="ButtonText" Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)" To="#b5b5b5"/>
                                <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(Effect).(DropShadowEffect.Opacity)" To="0"/>
                            </Storyboard>
                        </VisualState>
 
                        <VisualState Name="Disabled">
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="#697785"/>
                                <ColorAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(BorderBrush).(SolidColorBrush.Color)" To="#697785"/>
                                <ColorAnimation Storyboard.TargetName="ButtonText" Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)" To="#ececec"/>
                                <DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="(Effect).(DropShadowEffect.Opacity)" To="0"/>
                            </Storyboard>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
            </Border>
        </ControlTemplate>
 
        <Style x:Key="BlueButtons" TargetType="Button">
            <Setter Property="Background" Value="#34495e"/>
            <Setter Property="BorderBrush" Value="#34495e"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Foreground" Value="WhiteSmoke"/>
            <Setter Property="Template" Value="{StaticResource NumPadButtons}"/>
            <Setter Property="Focusable" Value="False"/>
        </Style>
         
        <Style x:Key="NumberButtons" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="WhiteSmoke"/>
            <Setter Property="BorderBrush" Value="WhiteSmoke"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Foreground" Value="#cc6023"/>
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="Template" Value="{StaticResource NumPadButtons}"/>
            <Setter Property="Focusable" Value="False"/>
        </Style>
         
        <Style x:Key="MenuBar" TargetType="Menu">
            <Setter Property="Background" Value="#cc6023"/>
            <Setter Property="Menu.Effect">
                <Setter.Value>
                    <DropShadowEffect Color="Black" Direction="270" ShadowDepth="1" BlurRadius="4" Opacity="0.35"/>
                </Setter.Value>
            </Setter>
        </Style>
         
        <Style x:Key="MenuBarItem" TargetType="MenuItem">
            <Setter Property="Foreground" Value="WhiteSmoke"/>
        </Style>
 
        <Style x:Key="SubMenuItem" TargetType="MenuItem">
            <Setter Property="Foreground" Value="#cc6023"/>
        </Style>
    </Window.Resources>
     
    <DockPanel>
        <Menu IsMainMenu="True" DockPanel.Dock="Top" Canvas.ZIndex="99" Style="{StaticResource MenuBar}">
            <MenuItem x:Name="MenuView" Header="_View" Style="{StaticResource MenuBarItem}">
                <MenuItem x:Name="MenuFormat" Header="Traditional _Format" InputGestureText="Ctrl+F" IsCheckable="True" IsChecked="False" Style="{StaticResource SubMenuItem}"/>
                <MenuItem x:Name="MenuDays" Header="Sho_w Days" InputGestureText="Ctrl+W" IsCheckable="true" IsChecked="false" Style="{StaticResource SubMenuItem}" />
                <Separator Margin="0,-3,0,-2"/>
                <MenuItem x:Name="MenuAvg" Header="Calculate Avera_ge" InputGestureText="Ctrl+G" IsCheckable="true" IsChecked="false" Style="{StaticResource SubMenuItem}" />
            </MenuItem>
 
            <MenuItem x:Name="MenuEdit" Header="_Edit" Style="{StaticResource MenuBarItem}">
                <MenuItem x:Name="MenuCE" Style="{StaticResource SubMenuItem}" />
                <MenuItem x:Name="MenuClr" Style="{StaticResource SubMenuItem}" />
            </MenuItem>
 
            <MenuItem x:Name="MenuTopHelp" Header="_Help" Style="{StaticResource MenuBarItem}">
                <MenuItem x:Name="MenuHelp" Style="{StaticResource SubMenuItem}" />
                <Separator Margin="0,-3,0,-2"/>
                <MenuItem x:Name="MenuAbout" Style="{StaticResource SubMenuItem}" />
            </MenuItem>
        </Menu>
        <Grid Background="#b1b9be">
            <Grid.RowDefinitions>
                <RowDefinition Height="66"/>
                <RowDefinition Height="Auto" MinHeight="180"/>
            </Grid.RowDefinitions>
            <Grid Height="50" Margin="13,13,13,0" VerticalAlignment="Top" Background="#669435" ClipToBounds="True">
                <Grid.RowDefinitions>
                    <RowDefinition Height="21"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Border BorderBrush="#FFACACAC" BorderThickness="1" Height="50" Grid.RowSpan="2" Margin="0,-2,0,-2">
                    <Border.Effect>
                        <DropShadowEffect Color="Black" ShadowDepth="0" BlurRadius="5" Opacity=".75"/>
                    </Border.Effect>
                </Border>
                <TextBlock x:Name="OpBlock" TextWrapping="NoWrap" Text="" TextAlignment="Right" ScrollViewer.VerticalScrollBarVisibility="Disabled" Margin="7,3,7,0" FontSize="9" HorizontalAlignment="Right"/>
                <TextBlock x:Name="InOutBlock" TextWrapping="NoWrap" Text="0" ScrollViewer.VerticalScrollBarVisibility="Disabled" VerticalAlignment="Center" TextAlignment="Right" Grid.Row="1" FontSize="16" FontFamily="Segoe UI Semibold" Margin="7,0"/>
                <TextBox x:Name="InOutBox" TextWrapping="Wrap" Text="0" ScrollViewer.VerticalScrollBarVisibility="Disabled" VerticalAlignment="Top" TextAlignment="Right" Grid.Row="1" FontSize="16" FontFamily="Segoe UI Semibold" Margin="7,0" IsReadOnly="True" Visibility="Collapsed"/>
            </Grid>
            <Grid Margin="11,0,11,11" Grid.Row="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="40"/>
                    <ColumnDefinition Width="40"/>
                    <ColumnDefinition Width="40"/>
                    <ColumnDefinition Width="40"/>
                    <ColumnDefinition Width="40"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="33"/>
                    <RowDefinition Height="33"/>
                    <RowDefinition Height="33"/>
                    <RowDefinition Height="33"/>
                    <RowDefinition Height="33"/>
                </Grid.RowDefinitions>
                <Button x:Name="BackButton" Content="←" Margin="0" Width="36" Height="29" FontSize="16" FontWeight="Bold" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="CEButton" Content="CE" Margin="0" Width="36" Height="29" Grid.Column="1" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="CButton" Content="C" Margin="0" Width="36" Height="29" Grid.Column="2" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="MinusButton" Content="-" Margin="0" Width="36" Height="29" Grid.Column="3" Style="{StaticResource BlueButtons}" FontSize="16"/>
                <Button x:Name="PMButton" Content="±" Margin="0" Width="36" Height="29" Grid.Column="4" Style="{StaticResource BlueButtons}" IsEnabled="False"/>
                <Button x:Name="SevenButton" Content="7" Margin="0" Width="36" Height="29" Grid.Column="0" Grid.Row="1" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="EightButton" Content="8" Margin="0" Width="36" Height="29" Grid.Column="1" Grid.Row="1" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="NineButton" Content="9" Margin="0" Width="36" Height="29" Grid.Column="2" Grid.Row="1" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="AddButton" Content="+" Margin="0" Width="36" Height="62" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2" FontSize="24" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="Blank1Button" Content="" Margin="0" Width="36" Height="29" Grid.Column="4" Grid.Row="1" IsEnabled="False" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="FourButton" Content="4" Margin="0" Width="36" Height="29" Grid.Column="0" Grid.Row="2" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="FiveButton" Content="5" Margin="0" Width="36" Height="29" Grid.Column="1" Grid.Row="2" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="SixButton" Content="6" Margin="0" Width="36" Height="29" Grid.Column="2" Grid.Row="2" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="Blank2Button" Content="" Margin="0" Width="36" Height="29" Grid.Column="4" Grid.Row="2" IsEnabled="False" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="OneButton" Content="1" Margin="0" Width="36" Height="29" Grid.Column="0" Grid.Row="3" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="TwoButton" Content="2" Margin="0" Width="36" Height="29" Grid.Column="1" Grid.Row="3" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="ThreeButton" Content="3" Margin="0" Width="36" Height="29" Grid.Column="2" Grid.Row="3" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="EqualButton" Content="=" Margin="0" Width="36" Height="62" Grid.Column="3" Grid.Row="3" Grid.RowSpan="2" FontWeight="Bold" FontSize="24" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="Blank3Button" Content="" Margin="0" Width="36" Height="29" Grid.Column="4" Grid.Row="3" IsEnabled="False" Style="{StaticResource BlueButtons}"/>
                <Button x:Name="ZeroButton" Content="0" Margin="0" Width="76" Height="29" Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="2" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="PeriodButton" Content="." Margin="0" Width="36" Height="29" Grid.Column="2" Grid.Row="4" Style="{StaticResource NumberButtons}"/>
                <Button x:Name="Blank4Button" Content="" Margin="0" Width="36" Height="29" Grid.Column="4" Grid.Row="4" IsEnabled="False" Style="{StaticResource BlueButtons}"/>
            </Grid>
        </Grid>
    </DockPanel>
</Window>
'@


#About Window XAML
[xml]$aboutXaml = @'
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="About Time Calculator" ResizeMode="NoResize" SizeToContent="WidthAndHeight">
 
    <Window.Resources>
        <Style x:Key="{x:Type Hyperlink}" TargetType="{x:Type Hyperlink}">
            <Setter Property="Foreground">
                <Setter.Value>
                    <SolidColorBrush Color="#A1CD71"/>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Foreground">
                        <Setter.Value>
                            <SolidColorBrush Color="#C7E1AB"/>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
 
    <StackPanel Width="292">
        <DockPanel>
            <Image Source="" Width="48" Height="48" Margin="25,20,15,20" Name="AboutIcon" />
            <TextBlock TextWrapping="Wrap" Text="Time Calculator" FontSize="20" Margin="0" VerticalAlignment="Center" Name="AboutHeaderTitle">
                <TextBlock.Foreground>
                    <SolidColorBrush Color="#cc6023"/>
                </TextBlock.Foreground>
            </TextBlock>
        </DockPanel>
        <StackPanel Background="#34495e">
            <TextBlock TextWrapping="Wrap" Margin="25,20,0,20" FontSize="14">
                <TextBlock.Foreground>
                    <SolidColorBrush Color="Gainsboro"/>
                </TextBlock.Foreground>
 
                <Run Text="TimeCalculator.ps1"/><LineBreak/>
                <Run FontSize="12" Text="Version: 2.5"/><LineBreak/>
                <Run FontSize="12" Text="License: TechNet"/><LineBreak/>
                <Run FontSize="12" Text="© 2015 "/>
                <Hyperlink x:Name="ProfileLink" NavigateUri="https://social.technet.microsoft.com/profile/chris%20carter%2079/">Chris Carter</Hyperlink>
            </TextBlock>
        </StackPanel>
    </StackPanel>
 
</Window>
'@


#Help Window XAML
[xml]$helpXaml = @'
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Help" Height="350" Width="366" ResizeMode="CanResize" MinWidth="366" MaxWidth="500" MinHeight="353" MaxHeight="600">
    <DockPanel HorizontalAlignment="Left" VerticalAlignment="Top">
        <DockPanel DockPanel.Dock="Top">
            <Image Source="" Width="32" Height="32" Margin="25,25,10,15" Name="HelpHeaderIcon" />
            <TextBlock TextWrapping="Wrap" Text="Time Calculator Help" VerticalAlignment="Center" FontSize="16" Foreground="#cc6023" Name="HelpHeaderTitle"/>
        </DockPanel>
        <ScrollViewer x:Name="SVHelp" VerticalScrollBarVisibility="Auto" DockPanel.Dock="Top">
            <ScrollViewer.OpacityMask>
                <LinearGradientBrush EndPoint="0,1">
                    <GradientStop Color="Black" Offset="0.8"/>
                    <GradientStop Color="Transparent" Offset="0.99"/>
                </LinearGradientBrush>
            </ScrollViewer.OpacityMask>
            <DockPanel Margin="0,0,0,25">
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,5" Text="Description" FontSize="14" FontWeight="Bold" DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,10" Text="Time Calculator is a PowerShell script for adding and subtracting spans of time. For example, adding 2 hours 47 minutes and 35 seconds with 1 hour 53 minutes and 2 seconds. It does not deal in adding time in regards to clock times, e.g., it will not add 43 minutes to 2:37 p.m." DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,5" Text="Entering Times" FontSize="14" FontWeight="Bold" DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,10" DockPanel.Dock="Top"><Run Text="Time is entered in the format:"/><LineBreak/><Run Text="hours:minutes:seconds with either a period or colon separating the fields. Single digits do not have to be preceded by a zero, and a zero does not have to be entered for an empty field, just the separator, e.g., ::1 is equivalent to 0:0:1 which is 1 second and :45 is equivalent to 0:45:0 which is 45 minutes."/></TextBlock>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,5" Text="Changing the Display Format" FontSize="14" FontWeight="Bold" DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,10" Text="The menu option &quot;Traditional Format&quot; toggles the output of the calculation between the default format of &quot;2 hrs 34 mins 5 secs&quot; and &quot;00:00:00&quot;." DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,5" Text="Showing Days" FontSize="14" FontWeight="Bold" DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,10" Text="By default the calculation does not display days in the output. This can be changed by toggling the &quot;Show Days&quot; menu option." DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,5" Text="Calculating Averages" FontSize="14" FontWeight="Bold" DockPanel.Dock="Top"/>
                <TextBlock TextWrapping="Wrap" Margin="25,0,25,10" Text="Toggling the menu option &quot;Calculate Average&quot; will cause the calculator to display the average time of the times listed. Any subtraction will be converted to addition when this is checked." DockPanel.Dock="Top"/>
            </DockPanel>
        </ScrollViewer>
    </DockPanel>
</Window>
'@


#RegEx matching the correct format of 'hours:minutes:seconds'
$regxpTime = '^(?<Hours>\d+)?(?:(?<hrsep>:|\.)?(?<Minutes>\d+)?(?:(?<minsep>:|\.)?(?<Seconds>\d+)?)?)?$'

#Create an Observable Collection that holds generic objects, PSCustomObject did not work.
$collOps = New-Object 'System.Collections.ObjectModel.ObservableCollection[System.Object]'

#Global variable to hold TimeSpan of calculation result
$calcResult = [TimeSpan]::Zero

#Load Windows for use
$xmlReader = New-Object System.Xml.XmlNodeReader -ArgumentList $xaml
$Window = [System.Windows.Markup.XamlReader]::Load($xmlReader)

#Load Controls and Resources by name
#Main Window Controls
$InOutBlock = $Window.FindName("InOutBlock")
$OpBlock = $Window.FindName("OpBlock")
$InOutBox = $Window.FindName("InOutBox")

$BackButton = $Window.FindName("BackButton")
$CEButton = $Window.FindName("CEButton")
$CButton = $Window.FindName("CButton")

$EqualButton = $Window.FindName("EqualButton")

$MenuFormat = $Window.FindName("MenuFormat")
$MenuDays = $Window.FindName("MenuDays")
$MenuAvg = $Window.FindName("MenuAvg")
$MenuCE = $Window.FindName("MenuCE")
$MenuClr = $Window.FindName("MenuClr")
$MenuHelp = $Window.FindName("MenuHelp")
$MenuAbout = $Window.FindName("MenuAbout")

$WrongToolTip = $Window.FindResource("WrongFormatToolTip")

#Add Event Handlers to Events
#Main Window Events
#Create array of button names, then use it to assign variables and attach event handlers
$btnNameArray = "Minus","Seven","Eight","Nine","Add","Four","Five","Six","One","Two","Three","Zero","Period"

foreach ($name in $btnNameArray) {
    $suffix = "Button"
    $tempButton = $Window.FindName("$name$suffix")
    Set-Variable -Name "$name$suffix" -Value $tempButton
    $tempButton.add_Click({On-NumPadClick})
}

$Window.add_TextInput({On-TextInput})
$Window.add_KeyUp({On-KeyUp})

$InOutBox.add_TextChanged({On-InOutTextChanged})

$BackButton.add_Click({Remove-LastCharacter; $_.Handled = $true})
$CEButton.add_Click({Remove-CurrentEntry; $_.Handled = $true})
$CButton.add_Click({Clear-All; $_.Handled = $true})

$EqualButton.add_Click({On-EqualClick})

$MenuFormat.add_Click({On-MenuFormatClick})
$MenuDays.add_Click({On-MenuDaysClick})
$MenuAvg.add_Click({On-MenuAvgClick})

#Add custom property to the input box for UI behaviors
$InOutBox | Add-Member -MemberType NoteProperty -Name IsTextFromCalculation -Value $false

#Custom Command Bindings

#Command for Clear All
#Build CanExecute and Execute Handlers
$ClearCanExec = {if ($InOutBox.Text -ne "0" -or $OpBlock.Text) {$_.CanExecute = $true}
                 else {$_.CanExecute = $false}}
$ClearExec = {Clear-All}

Add-CommandAndKeyBinding $ClearCanExec $ClearExec "D" ("Control","Shift") "Ctrl+Shift+D" "ClearAll" "Clear _All" $Window $MenuClr

#Command for Clear Entry
#Build CanExecute and Execute Handlers
$CECanExec = {if ($InOutBox.Text -ne "0") {$_.CanExecute = $true}
              else {$_.CanExecute = $false}}
$CEExec = {Remove-CurrentEntry}

Add-CommandAndKeyBinding $CECanExec $CEExec "D" "Control" "Ctrl+D" "ClearEntry" "Clear _Entry" $Window $MenuCE

#Commands for Checkable Menu Items. Toggle added to Executed block because keyboard shortcuts don't toggle IsChecked property.
#Command for Traditional Format
$AutoTrueCanExec = {$_.CanExecute = $true}
$FormatExec = {Toggle-CheckItem $MenuFormat
                On-MenuFormatClick}

Add-CommandAndKeyBinding $AutoTrueCanExec $FormatExec "F" "Control" "" "TradFormat" "Traditional _Format" $Window

#Command for Show Days
$DaysExec = {Toggle-CheckItem $MenuDays
             On-MenuDaysClick}

Add-CommandAndKeyBinding $AutoTrueCanExec $DaysExec "W" "Control" "" "ShowDays" "Sho_w Days" $Window

#Command for Calculate Average
$AvgExec = {Toggle-CheckItem $MenuAvg
            On-MenuAvgClick}

Add-CommandAndKeyBinding $AutoTrueCanExec $AvgExec "G" "Control" "" "CalcAvg" "Calculate Avera_ge" $Window

#Command for About
$AboutExec = {New-AboutDialogBox}

Add-CommandAndKeyBinding $AutoTrueCanExec $AboutExec "A" ("Control","Shift") "" "About" "_About" $Window $MenuAbout

#Command for Help
$HelpExec = {New-HelpDialogBox}

Add-CommandAndKeyBinding $AutoTrueCanExec $HelpExec "F1" "" "" "Help" "_View Help" $Window $MenuHelp

#End Custom Command Bindings

if ($icon) {$Window.Icon = $icon}

$Window.ShowDialog() | Out-Null