functions/archived-calendar-functions.txt

 
 
Return "These commands are archived and retained for testing and research purposes."
 
Function Get-NCalendar {
    [cmdletbinding()]
    [alias("ncal")]
    [Outputtype("String")]
 
    Param(
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Enter the full month name. The default is the current month."
            )]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
            #sometimes this returns an extra and blank entry
            $m = [system.globalization.cultureinfo]::CurrentCulture.DateTimeFormat.MonthNames | Where-Object { $_ }
            if ( $m -contains $_) {
                $True
            }
            Else {
                Throw "You must enter one of these values: $($m -join ',')"
                $False
            }
        })]
        [string]$Month = (Get-Date -Format MMMM),
        [Parameter(
            Position = 1,
            ValueFromPipelineByPropertyName,
            HelpMessage = "Enter the 4 digit year. The default is the current year."
            )]
        [ValidatePattern("\d{4}")]
        [ValidateRange(1000, 9999)]
        [int]$Year = (Get-Date).Year,
        [Parameter(HelpMessage = "Don't highlight the current date.")]
        [Switch]$HideHighlight,
        [Parameter(HelpMessage = "Start the week on Monday")]
        [Switch]$Monday
    )
 
    Begin {
        #display the module version defined in the psm1 file
        Write-Verbose "Starting $($myinvocation.MyCommand) [v$modver]"
        Write-Verbose "Using PowerShell version $($psversiontable.PSVersion)"
        #Call .NET for better results when testing this command in different cultures
        $currCulture = [system.globalization.cultureinfo]::CurrentCulture
    }
 
    Process {
        Write-Verbose "Using month $month and year $year"
 
        #get month number
        Write-Verbose "Parsing $month to number"
        $monthint = [datetime]::parse("1 $month $year").month
        Write-Verbose "Returned month number $monthint"
        $startd = [datetime]::new($year, $monthint, 1)
 
        $max = $currCulture.DateTimeFormat.Calendar.GetDaysInMonth($year, $monthint)
        Write-Verbose "Max days in month is $max."
 
        $daynames = $currCulture.DateTimeFormat.AbbreviatedDayNames
        $daylist = [System.Collections.Generic.list[string]]::new()
 
        if ($Monday) {
            Write-Verbose "Using a Monday-based week"
            ($daynames[1..6]).Foreach( { $daylist.add($_) })
            $daylist.Add($daynames[0])
        }
        else {
            $daylist.AddRange($daynames)
        }
 
        $daylist | ForEach-Object -Begin {
            $dayHash = [ordered]@{}
        } -Process {
            $dayHash.Add($_, @())
        }
 
        $today = (Get-Date).Date
        for ($i = 0; $i -lt $max; $i++) {
            $day = $startd.AddDays($i).date
            $dayname = "{0:ddd}" -f $day
            if ((-NOT $HideHighlight) -AND ($day -eq $today)) {
                $dom = "$([char]27)[7m$($day.day)$([char]27)[0m"
                $dayLength = $day.day.ToString().length
            }
            else {
                $dom = $day.day
            }
            $dayhash[$dayname] += $dom
        }
 
        $dayhash | Out-String | Write-Verbose
        #make sure month is in title case
        $head = "$($currculture.TextInfo.toTitleCase($Month)) $Year"
        $maxDayLength = $dayhash.keys.length | Sort-Object | Select-Object -Last 1
        Write-Verbose "Building day hashtable"
        $out = $dayhash.GetEnumerator() |
        ForEach-Object {
            Write-Verbose $_.name
            #build a value string
            $vstring = $_.value.foreach( {
                $str = $_.tostring()
                Write-Verbose "Day: $str"
                if ($str -match [char]27) {
                    #add extra padding to account for ANSI escape sequence
                    Write-Verbose "Adjusting for ANSI sequence"
                    Write-Verbose "String length = $($str.length)"
                    Write-Verbose "Saved length $ANSILength"
                    Write-Verbose "Day length = $daylength"
                    if ($daylength -le $str.length) {
                        $ansipad = $str.length - $daylength #8
                        #save ANSI length
                        $ANSILength = $str.length - $daylength
                    }
                    else {
                        $ansipad = 0
                    }
                    Write-Verbose "Padding $ansipad"
                }
                else {
                    $ansipad = 0
 
                }
                $str.padleft(1 + $ansipad)
            })
            Write-Verbose "Does day string contain ANSI?"
            Write-Verbose (($vstring -join ' ') -match [char]27)
            if ((($vstring -join ' ') -match [char]27)) {
                Write-Verbose "Adding $ANSILength to AnsiPad value"
                   $ansipad+= $ANSILength
            }
            Write-Verbose "Using days $($vstring -join ' ')"
            # Write-Verbose "Padding left $MaxDayLength + 12 + $ANSIPad"
            "{0}{1}" -f $_.name.Padright(4), $($vstring -join " ").padleft($maxDayLength + 12 + $ansipad)
            $ANSILength=0
        }
        Write-Verbose "display length = $($out[0].length)"
        #write-Verbose "head length = $($head.length)"
        $pad = (($out[0].length - $head.length) / 2) + $head.length + 1
        #Write-Verbose "padding $pad"
        $head.padleft($pad)
        $out
        #insert a blank line
        "`r"
    }
    End {
        Write-Verbose "Ending $($myinvocation.MyCommand)"
    }
}
 
Function Get-Calendar {
 
    [cmdletbinding(DefaultParameterSetName = "month")]
    [OutputType([System.String])]
    [Alias("cal")]
 
    Param(
        [Parameter(Position = 1, ParameterSetName = "month")]
        [ValidateNotNullorEmpty()]
        [ValidateScript( {
                $names = _getMonthsByCulture
                if ($names -contains $_) {
                    $True
                }
                else {
                    Throw "You entered an invalid month. Valid choices are $($names -join ',')"
                    $False
                }
            })]
        [string]$Month = (Get-Date -format MMMM),
 
        [Parameter(Position = 2, ParameterSetName = "month")]
        [ValidatePattern('^\d{4}$')]
        [int]$Year = (Get-Date).Year,
 
        [Parameter(Mandatory, HelpMessage = "Enter the start of a month like 1/1/2019", ParameterSetName = "span")]
        [ValidateNotNullOrEmpty()]
        [DateTime]$Start,
 
        [Parameter(Mandatory, HelpMessage = "Enter an ending date for the month like 2/1/2019", ParameterSetName = "span")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ($_ -ge $Start) {
                    $True
                }
                else {
                    Throw "The end date ($_) must be later than the start date ($start)"
                    $False
                }
            })]
        [DateTime]$End,
 
        [ValidateNotNullorEmpty()]
        [string[]]$HighlightDate = (Get-Date).date.toString()
    )
 
    Begin {
        Write-Verbose "Starting $($myinvocation.MyCommand)"
        Write-Verbose "Using PowerShell version $($psversiontable.PSVersion)"
        #Call .NET for better results when testing this command in different cultures
        $currCulture = [system.globalization.cultureinfo]::CurrentCulture
    }
    Process {
        Write-Verbose "Using parameter set: $($pscmdlet.ParameterSetName)"
        Write-Verbose "Using culture $($currCulture.name)"
        Write-Verbose "Using PSBoundParameters"
        Write-Verbose ($PSBoundParameters | Out-String).trim()
 
        if ($pscmdlet.ParameterSetName -eq "month") {
            Write-Verbose "Using month $month and year $year"
            Write-Verbose "Getting start date using pattern $($currCulture.DateTimeFormat.ShortDatePattern)"
 
            #get month number
            $monthint = [datetime]::parse("1 $month $year").month
            $start = [datetime]::new($year, $monthint, 1)
 
            $end = $start.date
        }
        else {
            #Figure out the first day of the start and end months
            $start = [datetime]::new($start.year, $start.Month, 1)
 
            $end = [datetime]::new($end.year, $end.month, 1)
 
        }
        Write-Verbose "Starting at $start"
        Write-Verbose "Ending at $End"
 
        #Convert the highlight dates into real dates
        #using the .NET Class to parse because this works better for culture-specific datetime strings
        [DateTime[]]$highlightDates = @()
        foreach ($item in $highlightDate) {
            Write-Verbose "Parsing $(($item | Out-String).trim()) to [datetime]"
            $item | Out-String | Write-Verbose
            $highlightDates += $item -as [datetime]
        }
 
        #re-add today if not one of the highlighted dates
        if ($highlightDates -notcontains ([datetime]::now).date) {
            Write-Verbose "Re-adding today to highlighted dates"
            $highlightDates += ([datetime]::now).date
        }
        Write-Verbose "Highlighting: $($highlightDates -join ',')"
        #Retrieve the DateTimeFormat information so that we can manipulate the calendar
        $dateTimeFormat = $currCulture.DateTimeFormat
        $firstDayOfWeek = $dateTimeFormat.FirstDayOfWeek
 
        Write-Verbose "First day of the week is $firstDayofWeek"
 
        $currentDay = $start
 
        Write-Verbose "Go through the requested months"
        while ($start -le $end) {
            #We may need to backpedal a bit if the first day of the month
            #falls in the middle of the week
            while ($currentDay.DayOfWeek -ne $dateTimeFormat.FirstDayOfWeek) {
                $currentDay = $currentDay.AddDays(-1)
            }
 
            #Prepare to store information about this date range
            Write-Verbose "Initializing currentweek"
            $currentWeek = New-Object -typename PsObject
            $dayNames = @()
            $weeks = @()
 
            Write-Verbose "Go until we've hit the end of the month."
            #Even once we've done that, continue until we fill up the week
            #with days from the next month.
            while (($currentDay -lt $start.AddMonths(1)) -or
                ($currentDay.DayOfWeek -ne $dateTimeFormat.FirstDayOfWeek)) {
                #Figure out the day names we'll be using to label the columns
                $dowlen = $dateTimeFormat.FirstDayOfWeek.length + 3
                $dayName = ("{0:ddd}" -f $currentDay).padleft($dowlen, ' ')
                if ($dayNames -notcontains $dayName) {
                    Write-Verbose "Adding $dayname"
                    $dayNames += $dayName
                }
 
                #Pad the day number for display, highlighting if necessary
                #get the length of the abbreviated weekday to know how much to pad
                $daypad = $daynames[0].length
 
                Write-Verbose "Padding $daypad"
                $displayDay = "{0,$daypad} " -f $currentDay.Day
 
                #See if we should highlight a specific date
                if ($highlightDates) {
                    $compareDate = New-Object DateTime $currentDay.Year, $currentDay.Month, $currentDay.Day
                    if ($highlightDates -contains $compareDate) {
                        $displayDay = "*" + ("{0,$($daypad-1)}" -f $currentDay.Day) + "*"
                    }
                }
 
                #Add in the day of week and day number as note properties.
                $currentWeek | Add-Member NoteProperty $dayName $displayDay
 
                # Write-Verbose "Move to the next day in the month"
                $currentDay = $currentDay.AddDays(1)
 
                #If we've reached the next week, store the current week
                #in the week list and continue on.
                if ($currentDay.DayOfWeek -eq $dateTimeFormat.FirstDayOfWeek) {
                    $weeks += $currentWeek
                    $currentWeek = New-Object -typename PsObject
                }
            }
 
            Write-Verbose "Format our weeks into a table"
            Write-Verbose ($weeks | Out-String)
            $calendar = $weeks | Format-Table -property $dayNames | Out-String
 
            Write-Verbose "Add a centered header"
            $width = ($calendar.Split("`n") | Measure-Object -Max Length).Maximum
            $header = "{0:MMMM yyyy}" -f $start
            Write-Verbose $header
            $padding = " " * (($width - $header.Length) / 2)
            #use this line to insert a blank line before the calendar
            Write-Verbose "Adding calendar"
            Write-Verbose $calendar
            $displayCalendar = " `n" + $padding + $header + "`n " + $calendar
 
            #use this line to not insert a blank line before the calendar
            #$displayCalendar = $padding + $header + "`n " + $calendar
            $displayCalendar.TrimEnd()
 
            #And now move onto the next month
            $start = $start.AddMonths(1)
        }
    } #process
 
    End {
        Write-Verbose "Ending $($myinvocation.MyCommand)"
    }
} #end Get-Calendar
 
 
#display a colorized calendar in the console
 
Function Show-Calendar {
 
    [cmdletbinding()]
    [Alias("scal")]
    [OutputType("None")]
 
    Param(
        [Parameter(Position = 1, ParameterSetName = "month")]
        [ValidateNotNullorEmpty()]
        [ValidateScript( {
                $names = _getMonthsByCulture
                if ($names -contains $_) {
                    $True
                }
                else {
                    Throw "You entered an invalid month. Valid choices are $($names -join ',')"
                    $False
                }
            })]
        [string]$Month = (Get-Date -format MMMM),
 
        [Parameter(Position = 2, ParameterSetName = "month")]
        [ValidatePattern('^\d{4}$')]
        [int]$Year = (Get-Date).Year,
 
        [string[]]$HighlightDate = (Get-Date).date.toString(),
 
        [Parameter(HelpMessage = "Specify a color for the highlighted days.")]
        [ValidateNotNullOrEmpty()]
        [consolecolor]$HighlightColor = "Green",
 
        [Parameter(HelpMessage = "Specify a color for the days of the month heading.")]
        [ValidateNotNullOrEmpty()]
        [consolecolor]$TitleColor = "Yellow",
 
        [Parameter(HelpMessage = "Specify a color for the days of the week heading.")]
        [ValidateNotNullOrEmpty()]
        [consolecolor]$DayColor = "Cyan",
 
        [Parameter(HelpMessage = "Specify a color to mark today")]
        [ValidateNotNullOrEmpty()]
        [consolecolor]$TodayColor = "Red",
        [System.Management.Automation.Host.Coordinates]$Position
    )
 
    Write-Verbose "Starting $($myinvocation.mycommand)"
 
    #get culture to see how long the first day of week is
    $currCulture = [system.globalization.cultureinfo]::CurrentCulture
    if ($position) {
        #save current cursor location
        $here = $host.ui.RawUI.CursorPosition
        # New-WPFMessageBox $here
        [void]$PSBoundParameters.remove("Position")
    }
 
    #add default values if not bound
    $params = "Month", "Year", "HighlightDate"
    foreach ($param in $params) {
        if (-not $PSBoundParameters.ContainsKey($param)) {
            $PSBoundParameters.Add($param, $((Get-Variable -Name $param).value))
        }
    }
 
    #remove color parameters if specified
    "HighlightColor", "TitleColor", "DayColor", "TodayColor" | ForEach-Object {
        if ($PSBoundParameters.Containskey($_)) {
            [void]$PSBoundParameters.Remove($_)
        }
    } #foreach color parameter
 
    $cal = Get-Calendar @PSBoundParameters
 
    Write-Verbose "Turn the calendar into an array of strings"
    $calarray = $cal.split("`n")
 
    # a regular expression pattern to match on highlighted days
    [regex]$m = "(\*)?[\s|\*]\d{1,2}(\*)?"
    # a regular expression pattern to get the month and year
    [regex]$moyr = "(?<month>\w+)\s(?<year>\d{4})"
    #go through each line and write it back to the console using Write-Host
    foreach ($line in $calarray) {
 
        if (($line -match "\w+") -AND ($moyr.IsMatch($line))) {
            #get the month
            $a = $moyr.Matches($line)
            #convert the month name into a month number
            $thisMonth = _getMonthnumber ($a.groups.where( { $_.name -eq 'month' }).value)
            $thisYear = $a.groups.where( { $_.name -eq 'year' }).value
            Write-Verbose "Currently processing $thismonth $thisyear from $($line.trim)"
            #write the line with the month and year
            if ($position) {
                $host.ui.RawUI.CursorPosition = $Position
            }
            Write-Host $line -ForegroundColor $TitleColor
        }
        elseif ($line -match "[a-zA-z]{2,3}| -") {
            #write the day names and underlines
            if ($Position) {
                $Position.y++
                $host.ui.RawUI.CursorPosition = $Position
            }
            Write-Verbose "Adding day names and underlines"
            Write-Host $line -ForegroundColor $DayColor
        }
        elseif ($line -match "\*") {
            #break apart lines with asterisks
            Write-verbose "Processing lines with *"
            $week = $line
            if ($position) {
                $Position.y++
                $host.ui.RawUI.CursorPosition = $Position
            }
            $m.Matches($week).Value | ForEach-Object {
                $day = "$_"
                #turn the value into a datetime object
                $dt = (Get-Date -Day ($day.replace('*', '').trim()) -Month $thisMonth -Year $thisYear).date
                Write-Verbose "Checking $dt"
                #pad based on the length of the day of week string
                $l = $currCulture.DateTimeFormat.AbbreviatedDayNames[0].length
 
                $spacer = " "
                if ($l -eq 2) {
                    $l += 2
                }
                elseif ($l -eq 3) {
                    $l++
                }
                  if ($dt -eq (Get-Date).date) {
                    #only highlight the first instance of the current day. Issue #20
                    Write-Verbose "Highlighting today $day"
                    Write-Host "$($day.replace('*','').padleft($l," "))$spacer" -NoNewline -ForegroundColor $TodayColor
                }
                elseif ($day -match "\*") {
                    Write-Verbose "Highlighting $day"
                    Write-Host "$($day.replace('*','').padleft($l," "))$spacer" -NoNewline -ForegroundColor $HighlightColor
                }
                else {
                    write-Verbose "Writing $day"
                    Write-Host "$(($day).PadLeft($l," "))$spacer" -nonewline
                }
                #if the day is the last day of the month, advance the month
                $daysInMonth = [datetime]::DaysInMonth($thisyear, $thisMonth)
                if ($dt.day -eq $daysInMonth) {
                    Write-Verbose "Detected the end of the month"
                    if ($thismonth -eq 12) {
                        $thismonth = 1
                        $thisYear++
                    }
                    else {
                        $thismonth++
                    }
                }
            } #foreach week match value
            Write-Host ""
        }
        else {
            if ($Position) {
                $Position.y++
                $host.ui.RawUI.CursorPosition = $Position
            }
            Write-Verbose "No formatting needed for $line"
            Write-Host $line
        }
    } #foreach line in calarray
 
    if ($Position) {
        #set cursor position back
        $host.ui.RawUI.CursorPosition = $here
    }
 
    Write-Verbose "Ending $($myinvocation.mycommand)"
 
} #end Show-Calendar
 
#create a WPF-based calendar
Function Show-GuiCalendar {
    [cmdletbinding()]
    [OutputType("None")]
    [Alias("gcal")]
 
    Param(
        [Parameter(Position = 1, HelpMessage = "Enter the first month to display by date, like 1/1/2019.")]
        [ValidateNotNullOrEmpty()]
        [datetime]$Start = [datetime]::new([datetime]::now.year, [datetime]::now.month, 1),
 
        [Parameter(Position = 2, HelpMessage = "Enter the last month to display by date, like 3/1/2019. You cannot display more than 3 months.")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                if ($_ -ge $Start) {
                    $True
                }
                else {
                    Throw "The end date ($_) must be later than the start date ($start)"
                    $False
                }
            })]
        [datetime]$End = [datetime]::new([datetime]::now.year, [datetime]::now.month, 1),
 
        [Parameter(HelpMessage = "Enter an array of dates to highlight like 12/25/2019.")]
        [datetime[]]$HighlightDate,
 
        [Parameter(HelpMessage = "Select a font family for your calendar" )]
        [ValidateSet("Segoi UI", "QuickType", "Tahoma", "Lucida Console", "Century Gothic")]
        [string]$Font = "Segoi UI",
 
        [Parameter(HelpMessage = "Select a font style for your calendar." )]
        [ValidateSet("Normal", "Italic", "Oblique")]
        [string]$FontStyle = "Normal",
 
        [Parameter(HelpMessage = "Select a font weight for your calendar." )]
        [ValidateSet("Normal", "DemiBold", "Light", "Bold")]
        [string]$FontWeight = "Normal"
    )
 
    Write-Verbose "Starting $($myinvocation.mycommand)"
 
    #add the necessary type library and bail out if there are errors which means the
    #platform lacks support for WPF
 
    Try {
        Add-Type -AssemblyName PresentationFramework -ErrorAction Stop
        Add-Type –AssemblyName PresentationCore -ErrorAction Stop
    }
    Catch {
        Write-Warning "Failed to load a required type library. Your version of PowerShell and or platform may not support WPF. $($_.exception.message)"
        #bail out of the command
        Return
    }
 
    $months = do {
        $start
        $start = $start.AddMonths(1)
    } while ($start -le $end)
 
    if ($months.count -gt 3) {
        Write-Warning "You can't display more than 3 months at a time with this command."
        #bail out
        Return
    }
 
    #the title won't normally be seen but is set for development and test purposes
    $myParams = @{
        Months = $months
        Height = (200 * $months.count)
        Title = "My Calendar"
        HighlightDate = $HighlightDate
        Font = $Font
        FontStyle = $FontStyle
        FontWeight = $FontWeight
    }
 
    Write-Verbose "Using these parameters"
    $myparams | Out-String | Write-Verbose
 
    $newRunspace = [RunspaceFactory]::CreateRunspace()
    if ($newRunspace.ApartmentState) {
        $newRunspace.ApartmentState = "STA"
    }
    else {
        #This command probably won't run if the ApartmentState can't be set to STA
        #clean up
        $newRunspace.dispose()
 
        Write-Warning "Incompatible runspace detected. This command will most likely fail on this platform with this version of PowerShell."
        #bail out of the command
        return
    }
    $newRunspace.ThreadOptions = "ReuseThread"
    $newRunspace.Open()
 
    Write-Verbose "Defining runspace script"
 
    $psCmd = [PowerShell]::Create().AddScript( {
 
            Param (
                [datetime[]]$HighlightDate,
                [string]$Font,
                [string]$FontStyle,
                [string]$FontWeight,
                [int]$Height,
                [string]$Title,
                [datetime[]]$Months
            )
 
            #create a window form.
            $form = New-Object System.Windows.Window
 
            $form.AllowsTransparency = $True
            $form.WindowStyle = "none"
            #the title won't be shown when window style is set to none
            $form.Title = $Title
            $form.Height = $height
            $form.Width = 200
 
            $bg = New-Object System.Windows.Media.SolidColorBrush
 
            $form.Background = $bg
            #color is set for development purposes. It won't be seen normally.
            # $form.Background.Color = "green"
            # $form.background.Opacity = 0
            $form.ShowInTaskbar = $False
            $form.Add_Loaded( {
                    $form.Topmost = $True
                    $form.Activate()
                })
 
            $form.Add_MouseLeftButtonDown( { $form.DragMove() })
 
            #add event handlers to adjust opacity by using the +/- keys
            $form.add_KeyDown( {
                    switch ($_.key) {
                        { 'Add', 'OemPlus' -contains $_ } {
                            foreach ($cal in $myCals) {
                                If ($cal.Opacity -lt 1) {
                                    $cal.Opacity = $cal.opacity + .1
                                    $cal.UpdateLayout()
                                }
                            }
                        }
                        { 'Subtract', 'OemMinus' -contains $_ } {
                            foreach ($cal in $myCals) {
                                If ($cal.Opacity -gt .2) {
                                    $cal.Opacity = $cal.Opacity - .1
                                    $cal.UpdateLayout()
                                }
                            }
                        }
                    }
                })
 
            $stack = $stack = New-Object System.Windows.Controls.StackPanel
            $stack.Width = $form.Width
            $stack.Height = $form.Height
            $stack.HorizontalAlignment = "center"
            $stack.VerticalAlignment = "top"
 
            #create an array to store calendars so that opacity can be
            #set for multiple calendars in unison
            $myCals = @()
            foreach ($month in $months) {
                $cal = New-Object System.Windows.Controls.Calendar
 
                $cal.DisplayMode = "Month"
 
                <#
                notes for future development
                $calbg = new-object System.Windows.Media.ImageBrush
                $calbg.Opacity = "0.3"
                $calbg.ImageSource = "c:\scripts\zazu.gif"
                $cal.Background = $calbg
 
                $calbg = [System.Windows.Media.Brushes]::Aquamarine
                $cal.Background =$calbg
 
                #>
                $cal.Opacity = 1
                $cal.FontFamily = $font
                $cal.FontSize = 24
                $cal.FontWeight = $FontWeight
                $cal.FontStyle = $fontStyle
 
                $cal.DisplayDateStart = $month
                #added to allow display of past months
                $totaldays = [datetime]::DaysInMonth($month.year, $month.Month)
                $cal.DisplayDateEnd = $month.AddDays($totaldays - 1)
 
                $cal.HorizontalAlignment = "center"
                $cal.VerticalAlignment = "top"
 
                $cal.SelectionMode = "multipleRange"
                if ($highlightdate) {
                    foreach ($d in $HighlightDate) {
                        if ($d.month -eq $month.Month) {
                            $cal.SelectedDates.add($d)
                        }
                    }
                }
 
                $cal.add_DisplayDateChanged( {
                        # add the selected days for the currently displayed month
                        [datetime]$month = $cal.Displaydate
                        if ($highlightdate) {
                            foreach ($d in $HighlightDate) {
                                if ($d.month -eq $month.Month) {
                                    $cal.SelectedDates.add($d)
 
                                }
                            }
                        }
                        $cal.UpdateLayout()
                    })
 
                $stack.addchild($cal)
                $myCals += $cal
            } #foreach month
 
            $btn = New-Object System.Windows.Controls.Button
            $btn.Content = "_Close"
            $btn.Width = 75
            $btn.VerticalAlignment = "Bottom"
            $btn.HorizontalAlignment = "Center"
            $btn.Opacity = 1
            $btn.Add_click( {
                    $form.close()
                })
 
            $stack.AddChild($btn)
 
            $form.AddChild($stack)
            [void]$form.ShowDialog()
        }) #addScript
 
    [void]$psCmd.AddParameters($myparams)
    $psCmd.Runspace = $newRunspace
    Write-Verbose "Invoking calendar runspace"
    $handle = $psCmd.BeginInvoke()
 
    Write-Verbose "Creating ThreadJob"
    #calling a private, helper function which will clean up the runspace after the calendar is closed.
    $job = New-RunspaceCleanupJob -Handle $handle -PowerShell $pscmd -SleepInterval 30 -Passthru
    Write-Verbose "...Job Id $($job.id)"
    Write-Verbose "Ending $($myinvocation.mycommand)"
 
} #close Show-GuiCalendar
 
#region private functions
 
#a helper function to retrieve names
function _getMonthsByCulture {
    [cmdletbinding()]
    Param([string]$Culture = ([system.threading.thread]::currentThread).CurrentCulture)
    Write-Verbose "Getting months for culture $Culture"
    [cultureinfo]::GetCultureInfo($culture).DateTimeFormat.Monthnames
}
 
function _getMonthNumber {
    [cmdletbinding()]
    Param([string]$MonthName)
 
    _getMonthsByCulture | ForEach-Object -begin { $i = 0 } -process { $i++; if ($_ -eq $MonthName) { return $i } }
}
 
Function New-RunspaceCleanupJob {
    <#
    You use this function like this:
    $newrunspace = <code>
    $pscmd = [powershell]::create()
 
    add commands to $pscmd
    $pscmd.runspace = $newrunspace
    $handle = $pscmd.beginInvoke()
 
    Start a thread job to test if runspace is being used and close it if it is finished
    New-RunspaceCleanUpJob -handle $handle -powershell $pscmd -sleepinterval 30
    #>
    [cmdletbinding()]
    [OutputType("None", "ThreadJob")]
    Param(
        [Parameter(Mandatory, HelpMessage = "This should be the System.Management.Automation.Runspaces.AsyncResult object from the BeginInvoke() method.")]
        [ValidateNotNullorEmpty()]
        [object]$Handle,
        [Parameter(Mandatory)]
        [ValidateNotNullorEmpty()]
        [System.Management.Automation.PowerShell]$PowerShell,
        [Parameter(HelpMessage = "Specify a sleep interval in seconds")]
        [ValidateRange(5, 600)]
        [int32]$SleepInterval = 10,
        [Parameter(HelpMessage = "Pass the thread job object to the pipeline")]
        [switch]$Passthru
    )
 
    $job = Start-ThreadJob -ScriptBlock {
        param($handle, $ps, $sleep)
        #the Write-Host lines are so that if you look at the results of the thread job
        #you'll see something you can use for debugging or troubleshooting.
        Write-Host "[$(Get-Date)] Sleeping in $sleep second loops"
        Write-Host "Watching this runspace"
        Write-Host ($ps.runspace | Select-Object -property * | Out-String)
        #loop until the handle shows as completed, sleeping the the specified
        #number of seconds
        do {
            Start-Sleep -Seconds $sleep
        } Until ($handle.IsCompleted)
        Write-Host "[$(Get-Date)] Closing runspace"
 
        $ps.runspace.close()
        Write-Host "[$(Get-Date)] Disposing runspace"
        $ps.runspace.Dispose()
        Write-Host "[$(Get-Date)] Disposing PowerShell"
        $ps.dispose()
        Write-Host "[$(Get-Date)] Ending job"
    } -ArgumentList $Handle, $PowerShell, $SleepInterval
 
    if ($passthru) {
        #Write the ThreadJob object to the pipeline
        $job
    }
}
 
#endregion