Windows PowerShell每周提示(32):创建图形化日期选取器
你知道,几乎每个人都有某种程度上的弱点。对超人来说,他的弱点是氪晶体。如果暴露在氪晶体之下,那么将会使得超人变得像一个孩子那样虚弱。对神力女超人而言,她的弱点是她自己的魔术套索,如果你能设法使用她自己的套索将她捆绑起来的话,那么她彻底会变得很无能。绿巨人则在颜色是黄色的事物面前变得很无力。脚本专家对氪晶体,任何颜色是黄色事物也感到畏惧。但是除此之外,脚本而专家也有另外一个弱点:需要用户输入日期的脚本。
这不是说写这些脚本很难,难的是控制用户所输入日期的格式。例如,假设用户输入了类似以下格式的日期:
这看上去很明确:明显是2008年3月11日。
当然,他也可能代表2008年11月3日。毕竟在世界上的许多地方,标准的日期格式是:
你如何知道这个时间在这里具体代表什么呢?你并不知道。
有写日期也并非真正意义上的日期,你对像April 31, 2008这类的日期如何处理呢?
有时用户也会发生拼写错误,我也许知道Febuary 3, 2008是什么意思,但是计算机不会。当然,也还有其它情况……你知道我们想说什么了。任何时候你要求用户在命令提示符中输入日期也就意味着你在自找麻烦。
而这个问题的解决方法事实上也很简单:弹出一个日历并让用户从日历中选择日期。这解决了格式的问题,计算机将会将用户选择的日期转换为正确的格式。这也同时解决了用户选择一个像4月31日这样不存在的日期所带来的问题,也解决了月份名称拼写错误的问题。然而,事实上还有一个问题没解决:在Windows PowerShell中没有办法弹出日历。
这意味着我们唯一能做的事是——等一下,谁说不能在Windows PowerShell中弹出日历的呢?
| [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") $objForm = New-Object Windows.Forms.Form $objForm.Text = "Select a Date" $objForm.Size = New-Object Drawing.Size @(190,190) $objForm.StartPosition = "CenterScreen" $objForm.KeyPreview = $True $objForm.Add_KeyDown({ if ($_.KeyCode -eq "Enter") { $dtmDate=$objCalendar.SelectionStart $objForm.Close() } }) $objForm.Add_KeyDown({ if ($_.KeyCode -eq "Escape") { $objForm.Close() } }) $objCalendar = New-Object System.Windows.Forms.MonthCalendar $objCalendar.ShowTodayCircle = $False $objCalendar.MaxSelectionCount = 1 $objForm.Controls.Add($objCalendar) $objForm.Topmost = $True $objForm.Add_Shown({$objForm.Activate()}) [void] $objForm.ShowDialog() if ($dtmDate) { Write-Host "Date selected: $dtmDate" } |
这究竟是什么呢,我们为什么会向你展示这个呢?让我们试着看下代码并看看我们的解释能不能回答你的问题。
一开始,我们需要加载.NET Framework类System.Windows.Forms;这是下面这行代码所完成的:
| [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") |
不要太担心这里的细节问题,只需照搬这行代码那么就不会有问题。我们需要注意的是当System.Windows.Forms加载时,[void]阻止了在屏幕上显示类的信息。如果我们不使用[void]的话,那么当我们执行LoadWithPartialName方法时,在屏幕上将会显示以下内容:
| GAC Version Location --- ------- -------- True v2.0.50727 C:\WINDOWS\assembly\GAC_MSIL\System.Windows.Forms\2.0.0.0__b77a5c561934e089\... |
这就是所谓“得到了我们真正所需要(或关心)的信息之外的更多内容”这个问题的范畴。我们随后使用了相同的方法来创建System.Drawing类的实例:
| [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") |
当这个两个类加载完成后,我们使用New-Object cmdlet来创建Forms类的新实例。(因为我们不得不用一个容器来放置日历控件,所以我们需要Forms类的一个实例。)在创建完空白窗体之后(使用对象引用$objForm),我们使用以下代码块来设置窗体的标题(Text)和大小(190像素*190像素),并确保窗体每次出现时都在屏幕中心:
| $objForm.Text = "Select a Date" $objForm.Size = New-Object Drawing.Size @(190,190) $objForm.StartPosition = "CenterScreen" |
我们能使窗体出现在屏幕上这很棒,当然如果能让窗体从屏幕上消失就更好了。毕竟,我们需要因为以下原因摆脱窗体:a)我们不想一直都在选取日期;b)我们获得日期之后,我们想要使用得到的日期来继续运行脚本。为了使事情事情变得尽可能的简单,我们决定让我们的窗体只能完成两件事:按下Enter键,窗体将完成与点击OK按钮相同的事,而按下ESC键则等同于按下Cancel键。
| 注意:我们能够在窗体上同时使用OK按钮和Cancel按钮么?当然,更多信息,请阅读上一次的Windows PowerShell每周提示。 |
为了使用像ENTER和ESC之类的键盘命令,我们首先要做的是将窗体的KeyPreview属性设为True,也就是通过以下这行代码来完成:
| $objForm.KeyPreview = $True |
我们需要定义如果用户按下ENTER键后所要执行的动作:
| $objForm.Add_KeyDown({ if ($_.KeyCode -eq "Enter") { $dtmDate=$objCalendar.SelectionStart $objForm.Close() } }) |
这里我们使用了Add_KeyDown方法来告诉窗体当用户按下ENTER键时所要做的(也就是刚按下键的Keycode为ENTER)。如果用户按下ENTER那我们将完成两件事:
首先,提取日历的SelectionStart属性值并将其储存在名为$dtmDate的变量中。这对提取用户选择的日期并将其储存在变量$dtmDate而言的确是一个很简单的方法。
其次,我们调用Close方法来关闭窗口。随后我们定义了如果用户按下ESC键后所要采取的行为:
| $objForm.Add_KeyDown({ if ($_.KeyCode -eq "Escape") { $objForm.Close() } }) |
注意,在这种情况下,我们不打算提取用户选择的日期。这是因为,用户通过按ESC键来告诉脚本他不想要选择一个日期。相反,我们脚本所要做的是关闭窗体。
此刻,我们才涉及到如何向窗体添加日历控件。为了完成这件事,我们需要创建System.Windows.Forms.MonthCalendar类的实例。然后我们使用两行代码来配置日历的两个属性:
| $objCalendar.ShowTodayCircle = $False $objCalendar.MaxSelectionCount = 1 |
我们将ShowTodayCircle属性设为False只是为了一个很简单的理由:因为我们不喜欢当这个属性为True时日历的外观。(两者之间的区别,请诸位自行尝试)。我们然后设置MaxSelectionCount属性为1,该属性限制用户只能在日历中选择单一日期。
| 注意:这是否意味着你能选择多个日期?事实上是可以的:你实际上能选择一个范围的日期。想要这么做的话,将MaxSelectionCount属性设为开始日期与结束日期之间的最大时间间隔。例如,如果你想要允许两个日期相差10天那么就将该属性设为10.然后使用SelectionStart属性来决定开始日期而SelectionEnd属性决定结束日期。 |
在配置完属性值之后我们使用Add方法来向窗体添加日历控件:
| $objForm.Controls.Add($objCalendar) |
然后我们设置TopMost属性,该属性保证窗体将出现在屏幕上其它窗体之前:
然后我们使用以下两行代码来实际展现我们的日历:
| $objForm.Add_Shown({$objForm.Activate()}) [void] $objForm.ShowDialog() |
日历应当看上去像这样:
顺便说一句,我们的日历一次只显示一个月是因为我们只想显示一个月。如果说我们想要一次看四个月?好的,在这种情况下,我们需要对脚本做两处修改。首先,我们需要增加窗体的大小:
| $objForm.Size = New-Object Drawing.Size @(370,330) |
其次,我们需要设置CalendarDimensions属性来使日历控件显示四个月。例如,以下命令展示创建了一个两行两列的日历:
| $objCalendar.CalendarDimensions = New-Object Drawing.Size @(2,2) |
该代码将使得日历看上去像这样:
如果你喜欢以当前月(2008年3月)启动日历,那么像这样设置MinDate属性:
| $objCalendar.MinDate = Get-Date |
等等。
当用户选择一个日期并按回车之后,我们所要做的是决定并回显选择的日期:
| if ($dtmDate) { Write-Host "Date selected: $dtmDate" } |
这里我们使用if语句来决定$dtmDate是否包含一个值,如果没有那么就意味着用户按了ESC键。如果有一个值,那么我们就只是回显该值,像这样:
如果你不喜欢这种格式,那么你可以使用你喜欢的格式。使用下面两行代码:
| $dtmDateLong = $dtmDate.ToLongDateString() $dtmDateShort = $dtmDate.ToShortDateString() |
第一行代码将$dtmDate转换至一下格式:
同时,第二行代码将显示以下格式的日期:
下面就看你的了。
我们可以一直继续下去,但是我们本周已经没时间了。但是不要担心,我们知道使用PowerShell创建GUI程序(像日期选取器)很有趣。因此我们正在编写一系列的例子,它们将向你展示如何弹出使用单选框,复选框,列表显示等等内容的窗体。请继续等待!
英文原文
http://www.microsoft.com/technet/scriptcenter/resources/pstips/mar08/pstip0314.mspx