Hey, Scripting Guy! Can I Use Windows PowerShell to Pick Start and End Dates?

Hey, Scripting Guy! Can I Use Windows PowerShell to Pick Start and End Dates?

  • Comments 1
  • Likes
Share this post:

Hey, Scripting Guy! Question

Hey, Scripting Guy! I need to be able to display a calendar so that I can pick starting dates and ending dates by using a graphical tool. This is much easier and faster than having to type two dates into the command line, and it is more accurate as well. Besides, when I am choosing dates, I always have to click the calendar in the lower right hand corner of my Windows 7 laptop to get a calendar anyway—it would be great if I could directly choose the dates from that. What do you think?

-- KB

Hey, Scripting Guy! Answer

Hello KB,

Microsoft Scripting Guy Ed Wilson here. I think that as soon as I am finished writing my Windows PowerShell Best Practices book for Microsoft Press, I am going to start studying for the Exam 70-680 that was released for Windows 7. I think it will be fun. There are no study materials available for it, so it will be just the computer and me. I will prepare for the exam by looking over the exam objectives and making sure I know how to do everything. I could probably pass it without doing too much studying because I have been using Windows 7 for more than a year, but there are some things that I want to brush up on before I go take the exam. 

Oh, KB, you were wondering what I think about accessing a calendar? I think that it would be great if I could write a script that would detect the dates you select on your calendar at the lower corner of your laptop screen. However, I do not know of a way to do that. What I can do is create a portable calendar that you can use to select start and end dates that will feed back to your script. The Get-DateRangeFunction.ps1 script, seen here, does that very thing.

Get-DateRangeFunction.ps1

Function Get-DateRange
{  
 [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
 out-null
 
 $WinForm = New-Object Windows.Forms.Form  
 $WinForm.text = "Calendar Control"  
 $WinForm.Size = New-Object Drawing.Size(411,190)

 $Calendar = New-Object System.Windows.Forms.MonthCalendar  
 $Calendar.MaxSelectionCount = 356    
 $Calendar.SetCalendarDimensions([int]2,[int]1)
 $WinForm.Controls.Add($Calendar)  
 
 $WinForm.Add_Shown($WinForm.Activate()) 
 $WinForm.showdialog() | Out-Null 
 $Calendar.SelectionRange
} #end function Get-DateRange

# *** Entry point to script ***
$dates = Get-DateRange
"Start Date: " + ($dates.Start).tostring("dd-MM-yyyy")
"End Date: " + ($dates.End ).tostring("dd-MM-yyyy")

The first thing we do in the Get-DateRangeFunction.ps1 script is create the Get-DateRange function. To do this, we use the Function keyword, and specify the Verb-Noun name pattern that is used by the Windows PowerShell cmdlets. In Windows PowerShell 2.0, if you use a name such as GetMyDate for your function, it will generate a warning message if you place this function in a module. Modules are a Windows PowerShell 2.0 feature that allow you to store functions in an easy-to-reuse fashion. When stored in modules, Windows PowerShell functions begin to act like cmdlets. In fact, the original name for advanced functions was script cmdlet. To get you ready for that experience, you should get into the habit of using the Verb-Noun naming convention for all of your functions. If you need inspiration or ideas you can use the Get-Command cmdlet and look at the functions, their names, and what the different verbs are doing. It is easier to read if you pipe the results to the Sort-Object and sort by verb. You might also consider selecting only the cmdlet name. This is seen here:

PS C:\> Get-Command | Sort-Object -Property verb | Select-Object –Property name

Because you are typing this at the Windows PowerShell console, you might want to shorten this a little bit by using the built-in aliases. The revised command is seen here:

PS C:\> gcm | sort verb | select name

In Windows PowerShell 2.0, there is cmdlet named Get-Verb that will display a listing of approved verbs and how they are to be used.

The command to create the Get-DateRange function and open the script block is seen here:

Function Get-DateRange

{  

In the Get-DateRange function, we use the LoadWithPartialName static method from the System.Reflection.Assembly .NET Framework class. This is done to make the System.Windows.Forms .NET Framework assembly available to the script. There are many assemblies that are not loaded by default by Windows PowerShell. This is done to increase the speed in which Windows PowerShell starts up and to reduce the memory footprint the application consumes. If you find yourself writing graphical Windows PowerShell scripts on a routine basis, you may want to load the System.Windows.Forms into your profile so that it will always be available. For more information about using the LoadWithPartialName static method, look at yesterday’s Hey, Scripting Guy! post.

 [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |

 Out-Null

Once the System.Windows.Forms .NET Framework assembly has been loaded, you can create the Windows.Forms.Form .NET Framework class. The calendar control you will create in today's script needs a form to host the control; therefore, you need to create the form first. To create the Windows.Forms.Form class, you use the New-Object cmdlet and supply the full name of the .NET Framework class. The newly created object is stored in the $winForm variable. This is seen here:

 $WinForm = New-Object Windows.Forms.Form  

Once you have created the form, you will want to assign a value to the text property. Because this script is demonstrating a calendar control, I decided to use that for the title. The text property is displayed in the top bar of the form. The property assignment is seen here:

 $WinForm.text = "Calendar Control"  

You now need to specify the size of the form. To specify the size of the form, you will need to supply the size property with an instance of a System.Drawing.Size structure. When you use the New-Object cmdlet to create the Drawing.Size structure, you supply the height and the width of the rectangle that will be created. This is seen here:

 $WinForm.Size = new-object Drawing.Size(411,190)

You use the New-Object cmdlet to create an instance of the System.Windows.Forms.MonthCalendar .NET Framework class. This class is used to create a calendar control that you will use on the form. You will need to save the returned object in the $calendar variable. This is seen here:

 $Calendar = new-object System.Windows.Forms.MonthCalendar  

By default, the maximum number of days you can select is set to 7. To modify this behavior assign a new value for the MaxSelectionCount property. In the Get-DateRangeFunction.ps1 script, I modify it to allow you to select 356 days if you are so inclined:

 $Calendar.MaxSelectionCount = 356    

The calendar dimensions control how the calendars will be displayed on your form. The SetCalendarDimensions method controls the number of columns and rows of calendars that will be displayed. In the Get-DateRangeFunction.ps1 script, I create a display of calendars that is two calendars wide, but only has a single row. This method call is seen here:

 $Calendar.SetCalendarDimensions([int]2,[int]1)

After the calendar dimensions have been set, it is time to add the Calendar to the Controls Collection of the form.  To do this, you use the Controls property from the Form class, and call the add method from the Windows.Forms.Form.Control.ControlClass. After you have added the Calendar to the Controls Collection, you call the Activate method from the Form class. This is seen here:

 $WinForm.Controls.Add($Calendar)  

 $WinForm.Add_Shown($WinForm.Activate()) 

You then call the ShowDialog method from the Form class to display the calendar. This is seen here:

 $WinForm.showdialog() | Out-Null 


When you pass your mouse over the calendar, you will select a range of dates. The range of dates is stored in the SelectionRange property of the Calendar control. It gets passed back to the calling code when you click the Close button in the upper right corner of the form. This is seen here:

 $Calendar.SelectionRange

} #end function Get-DateRange

When the Get-DateRange function is called, you are presented with a display that shows two calendars side by side. Drag your mouse to select a range of dates. This is seen in here:

Image of two calendars presented side by side

After you have clicked the Close button in the upper right corner of the dialog box, an instance of the System.Windows.Forms.SelectionRange class is returned to the calling code. In this example, I store the SelectionRange class in the $dates variable. I then take the Start date, and convert it to a string by using the ToString method. The ToString method will accept formatting to display the day, month, and year in a particular manner. The same thing is done with the end range. Both the Start property and the End property hold System.DateTime objects. The ToString method from the System.DateTime class is able to receive different parameters that you can use to modify the way the date is displayed. This is seen here:

$dates = Get-DateRange

"Start Date: " + ($dates.Start).tostring("dd-MM-yyyy")

"End Date: " + ($dates.End ).tostring("dd-MM-yyyy")

By using a DateTime format string or "dd-MM-yyyy" the day/month/year display seen here is produced:

Image of day/month/year display

KB, that is all there is to creating a calendar control and displaying it on a Windows form. Thank you for your question. This also wraps up the Graphical Windows PowerShell Week series of articles. Join us tomorrow as we dig into our e-mail bag and retrieve those questions that only need a short answer—it is time once again for Quick-Hits Friday.

To stay abreast of all the exciting things happening on the TechNet Script Center, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post it to the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Nice, would it be possible to get the range also with time? Something like from 08/27/2012 3:00PM to 08/27/2012 6:00PM.

    Thank you