本週 Windows PowerShell 秘訣

Office Space

這是使用 Windows PowerShell 的快速秘訣。只要有新的秘訣,我們每週就會在這裡發佈。如果您想要分享任何秘訣,或有任何疑問,請讓我們知道

您可以在本週 Windows PowerShell 秘訣封存找到更多秘訣。

建立圖形化日期選擇器

你知道…每個人都有弱點。對超人來說就是克利普頓石,只要暴露在克利普頓石之下,超人就會變得非常虛弱。對神力女超人來說就是她自己的神奇繩索,如果你想辦法用神奇繩索把她綁起來,她也會變得很虛弱。還有當綠燈俠碰到黃色物體時,他就會變得非常無力。那 Scripting Guys 呢?嗯,對 Scripting Guys 來說也是克利普頓石…和黃色物體。除此之外,Scripting Guys 還有一個弱點:讓使用者輸入日期的指令碼。

寫出這樣的指令碼並不難,困難的是要控制使用者輸入的日期格式。例如,假設使用者輸入下列日期:

3/11/2008

這似乎非常直截了當:明顯地是指 2008 年 3 月 11 日。

但也有可能是 2008 年 11 月 3 日,畢竟世界上許多地區的標準日期格式如下:

日/月/年

那您怎麼知道這裡使用哪一種格式呢?答案是你永遠不可能知道。

還有一些不是日期的日期,您要怎麼處理像是 2008 年 4 月 31 日這樣的日期呢?另外如果拼字有問題,我們雖然可能瞭解使用者鍵入「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 x 190 像素),並確認表單每次出現時,都會顯示在螢幕的中央:

$objForm.Text = "Select a Date" 
$objForm.Size = New-Object Drawing.Size @(190,190) 
$objForm.StartPosition = "CenterScreen"
    

可以讓表單出現在螢幕上真是一件好事。更好的是我們也可以讓表單從螢幕上「消失」。在下列兩種情況下,我們不需要看到表單:a) 我們根本就不想要挑選日期,或 b) 已經挑選好日期,現在要使用該日期繼續後續動作。為了不讓事情複雜化,我們決定只在表單的使用者介面放置兩個東西:按下 ENTER 鍵與按一下 [確定] 按鈕的功能相同。而按下 ESC 鍵與按下 [取消] 按鈕的功能相同。

注意:我們可以在表單上放置 [確定] 和 [取消] 按鈕嗎?當然可以!如需詳細資訊,請參閱我們之前的本週 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 的外觀(有什麼差別嗎?先試試將 ShowTodayCircle 設定為 $False,然後再嘗試將 ShowTodayCircle 設定為 $True,您就知道啦!)。接著,我們將 MaxSelectionCount 屬性設定為 1,這樣可以限制使用者只在日曆中選取一個日期。

注意:這表示可能讓使用者選取一個以上的日期囉?一點都沒錯:實際上你可以選取日期範圍。只要將 MaxSelectionCount 屬性設定為開始和結束日期之間的最大間隔即可。例如,如果兩個日期之間不超過 10 天,則你可以將 MaxSelectionCount 設定為 10。然後使用 SelectionStart 屬性來判斷開始日期,並使用 SelectionEnd 屬性來判斷結束日期。

設定完屬性值之後,我們使用 Add 方法將日曆新增至表單:

$objForm.Controls.Add($objCalendar)

接下來我們設定 TopMost 屬性,這樣可以確保表單會顯示在螢幕中其他視窗之上:

$objForm.Topmost = $True

然後使用下面兩行程式碼來實際叫用日曆:

$objForm.Add_Shown({$objForm.Activate()})  
[void] $objForm.ShowDialog()
    

日曆看起來應該如下圖:

Ff730942.calendar1(zh-tw,TechNet.10).jpg

我們的日曆一次只顯示一個月份,因為這是我們想要的。假設我們一次想看 4 個月份,該怎麼做呢?這時我們就需要修改指令碼的兩個地方。第一,要增加表單的大小:

$objForm.Size = New-Object Drawing.Size @(370,330)

第二,要將 CalendarDimensions 屬性設定為四的組合;例如,下面的命令會讓日曆以長寬各兩個月份的方式呈現:

$objCalendar.CalendarDimensions = New-Object Drawing.Size @(2,2)

結果,日曆就會如下所示:

Ff730942.calendar2(zh-tw,TechNet.10).jpg

如果您希望日曆以目前的月份 (2008 年 3 月) 開始,那請設定 MinDate 屬性,如下:

$objCalendar.MinDate = Get-Date

…等等…等等

注意:若需要日曆屬性和方法的完整清單,請參閱 .NET Framework SDK。

當使用者選擇日期並按下 ENTER 鍵之後,我們唯一要做的就是判斷 (並回應) 選取的日期:

if ($dtmDate)
    {
Write-Host "Date selected:$dtmDate"
    }
    

我們在這裡使用 if 陳述式來判斷 $dtmDate 是否含有值。如果沒有,表示使用者按了 ESC 鍵。如果含有值,則我們只要回應該值就好,如下:

03/18/2008 00:00:00

如果您不喜歡這種格式,你可把它修改成喜歡的格式。使用下面兩行程式碼:

$dtmDateLong = $dtmDate.ToLongDateString()
$dtmDateShort = $dtmDate.ToShortDateString()
    

第一行將 $dtmDate 轉換為下列格式:

Thursday, March 13, 2008

而第二行所得到的格式如下:

3/13/2008

全憑個人喜好。

我們可以繼續下去,不過本週的時間已經用完了。別擔心!我們知道使用 Windows PowerShell 來建立 GUI 小程式 (如日期選擇器) 有很多樂趣。因此,我們準備了一系列的範例來說明如何跳出使用核取方塊、選項按鈕和清單檢視的對話方塊方塊喔!請隨時留意我們的活動!