本週 Windows PowerShell 秘訣

Office Space

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

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

從清單方塊選取項目

我們必須承認:當您在 Windows PowerShell 中建立圖形使用者介面元件時,我們會稍微偷笑一下。畢竟,我們已經告訴您如何建立圖形日期選取器,而且已經告訴您如何建立輸入方塊。但至少有一件事 (一件「大」事) 我們尚未告訴您:如何使用 Windows PowerShell 建立可讓您從清單方塊選取項目的對話方塊。對一般系統管理員而言,那是比從行事曆選取日期更迫切的需求。

千萬別說 Scripting Guy 沒有將其讀者的話聽進去。(我們實際上「並未」聽到讀者的話;我們只是不想被說成那樣。)您想要知道從清單方塊選取項目的 PowerShell 指令碼嗎?好的;下面是可讓您從清單方塊選取項目的 PowerShell 指令碼:

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "Select a Computer"
$objForm.Size = New-Object System.Drawing.Size(300,200) 
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
{$x=$objListBox.SelectedItem;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
{$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$x=$objListBox.SelectedItem;$objForm.Close()})
$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Please select a computer:"
$objForm.Controls.Add($objLabel) 

$objListBox = New-Object System.Windows.Forms.ListBox 
$objListBox.Location = New-Object System.Drawing.Size(10,40) 
$objListBox.Size = New-Object System.Drawing.Size(260,20) 
$objListBox.Height = 80

[void] $objListBox.Items.Add("atl-dc-001")
[void] $objListBox.Items.Add("atl-dc-002")
[void] $objListBox.Items.Add("atl-dc-003")
[void] $objListBox.Items.Add("atl-dc-004")
[void] $objListBox.Items.Add("atl-dc-005")
[void] $objListBox.Items.Add("atl-dc-006")
[void] $objListBox.Items.Add("atl-dc-007")

$objForm.Controls.Add($objListBox) 

$objForm.Topmost = $True

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

$x

什麼?您說這樣看起來有點複雜嗎?嗯,可能有一點。不過,那是因為 PowerShell 沒有建立和顯示對話方塊的內建方法;因此要依賴 .NET Framework,而且 .NET Framework 偶爾「會有」一點兒複雜。不過,往好處想,這種指令碼多數是照本宣科的程式碼。(如果您不信,請將它與我們用來建立輸入方塊的指令碼比較一下。)更好的是,這個指令碼真的「可以」讓您從清單方塊選取電腦:

Ff730949.listbox2(zh-tw,TechNet.10).jpg

這樣有沒有讓您覺得比較值得啊?

此外,這個指令碼不如您想像中那麼複雜。實際上,這個指令碼以相當直接的方式作為開頭;我們使用這兩行 (正是照本宣科的程式碼) 來載入 .NET Framework 類別 System.Windows.FormsSystem.Drawing

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

順便一提,如果您正在思考 [void] 的用途,它只是在載入類別時,用來抑制畫面顯示任何訊息。若不要捨棄這些訊息,則每次載入 .NET 類別時,我們都會看到類似下面的內容:

GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\WINDOWS\assembly\GAC_MSIL\System.Windows.Forms\2.0.0.0__b77a5c561934e089\...


我們承認,知道這些資訊很好。但並非每次執行指令碼時,都需要看到這些資訊。

下一個工作是建立基本表單,用來放置我們所有的控制項 (例如按鈕與清單方塊);那就是下面程式碼區塊所做的事:

$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "Select a Computer"
$objForm.Size = New-Object System.Drawing.Size(300,200) 
$objForm.StartPosition = "CenterScreen"

看到沒?一點也不複雜嘛!在第 1 行中,我們只使用 New-Object cmdlet 來建立 System.Windows.Forms.Form 類別的例項。在接下來的三行中,我們所做的事,只不過是指定新表單上的三個屬性值:

  • Text:不論此名稱為何,這實際上是視窗的「標題」。

  • Size:這是表單的大小,以像素為單位。針對這個範例,它是一個 300 像素寬乘以 200 像素高的表單。

  • StartingPosition:事實上,我們「不必」設定這個屬性;如果不管它,Windows 就會在顯示表單時,挑一個位置放置表單。不過,將 StartingPosition 設定為 CenterScreen,每次載入我們的表單時,將自動顯示在螢幕中央。這似乎是顯示任何表單的好地方。

在此文章中,我們只會處理一些基本的表單與控制項屬性。如需詳細資訊,請參閱 MSDN 上的 .NET Framework 類別參照

在定義基本表單屬性之後,接著就是下面這段程式碼:

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
{$x=$objListBox.SelectedItem;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
{$objForm.Close()}})

我們在此所做的是要告訴表單,當使用者按下 ENTER 或 ESC 時該做什麼。若要如此,首先將表單的 KeyPreview 屬性設定為 True ($True);告訴表單攔截特定按鍵,而不是允許表單上的控制項使用那些按鍵。事實證明,我們要攔截的按鍵之一是 ENTER 鍵;這一行程式碼將 ENTER 鍵登錄為要攔截的鍵,然後指示表單在 ENTER 鍵被按下時應該採取的動作:

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
{$x=$objListBox.SelectedItem;$objForm.Close()}})

如您所見,我們使用 Add_KeyDown 方法,將 ENTER 鍵 ($_.KeyCode –eq "Enter") 新增到攔截清單中。此外。我們告訴表單在攔截到 ENTER 鍵時應該做什麼;如果發生那種狀況,我們要將變數 $x 的值,設定為清單方塊中所選項目的值 ($objListBox.SelectedItem)。此外,我們使用 Close 方法來關閉表單。那是用來判定清單方塊中選取哪個項目的簡易 (相當簡單) 方法:我們只是抓取項目的值,然後將該值存入變數中。如此我們的指令碼可以「記住」選取哪個項目,即使已經離開對話方塊之後也一樣;畢竟這個值將會安全地放入變數 $x 中。

然後,使用類似的程式碼將 ESC 鍵加入攔截清單中:

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
{$objForm.Close()}})

請注意,在此情況下,我們並未設定 $x 的值;而只是關閉表單。為什麼呢?答對了:因為 ESC 鍵相當於 [Cancel] (取消) 按鈕。換句話說,按下 ESC 表示我們要取消此操作,而不會從清單方塊擷取「任何」值。

很抱歉,清單方塊;有時候它的行為就是如此。

如同剛剛所提示,我們的表單包含 [Cancel] 按鈕;這應該一點都不意外,然後其中還包含 [OK] (確定) 按鈕。我們如何將 [OK] 按鈕加入表單中呢?以下是其中一種方式:

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$x=$objListBox.SelectedItem;$objForm.Close()})
$objForm.Controls.Add($OKButton)

無可否認地,新增按鈕需要一點功夫;您不是只做一些類似呼叫稱為 AddButton 的方法而已。不過,事情也不是表面上看起來那麼糟。若要新增按鈕,我們一開始先建立 System.Windows.Forms.Button 類別的例項。在建立按鈕之後,我們指定 Location 屬性的值 (在此情況下,我們的按鈕位於距離表單左側 75 像素及表單上方 120 像素的位置),然後指定按鈕的 Size 與 Text 屬性值。一旦完成此工作,我們使用這行程式碼來指出使用者按下此按鈕時發生的動作:

$OKButton.Add_Click({$x=$objListBox.SelectedItem;$objForm.Close()})

嗯!您真是觀察敏銳:我們想要它發生的事「剛好」與使用者按下 ENTER 時相同:我們要將在清單方塊中選取的值指定給 $x,然後關閉表單。為何這些命令完全相同?您又答對了:因為我們要它發生相同的事,不論使用者按下 ENTER 或 [OK]。那是典型的對話方塊行為,除了典型的行為之外,我們討厭任何形式的對話方塊。

設定所有的屬性值之後,我們立即使用 Add 方法將按鈕加入表單中:

$objForm.Controls.Add($OKButton)

然後重複此程序,將 [Cancel] 按鈕加入表單。

接著,使用非常類似的方法將標籤加入表單;也就是下面程式碼所做的事:

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Please select a computer:"
$objForm.Controls.Add($objLabel)

若要新增標籤,我們必須做的只是建立 System.Windows.Forms.Label 類別的例項,然後設定新控制項的屬性值。一旦完成該動作,我們再次使用 Add 方法將標籤加入表單。

您以為這很複雜吧!很慚愧!

現在我們的對話方塊包含標籤與一對按鈕。唯一遺漏的是:清單方塊本身。但沒關係;我們馬上要新增清單方塊了。

$objListBox = New-Object System.Windows.Forms.ListBox 
$objListBox.Location = New-Object System.Drawing.Size(10,40) 
$objListBox.Size = New-Object System.Drawing.Size(260,20) 
$objListBox.Height = 80

其中多數又看起來都類似;我們需要做的只是建立 System.Windows.Forms.ListBox 類別的新例項,然後設定 Location、Size 以及 Height 屬性的值。那將會給我們一個清單方塊,雖然只是一個空白的清單方塊,其中未選取任何項目。

您曉得那正是我們想要的:除非在清單方塊選取一些項目,否則它「有」何樂趣?這塊程式碼就是用來執行這項任務的:

[void] $objListBox.Items.Add("atl-dc-001")
[void] $objListBox.Items.Add("atl-dc-002")
[void] $objListBox.Items.Add("atl-dc-003")
[void] $objListBox.Items.Add("atl-dc-004")
[void] $objListBox.Items.Add("atl-dc-005")
[void] $objListBox.Items.Add("atl-dc-006")
[void] $objListBox.Items.Add("atl-dc-007")

不用說,這些程式碼一點「都不」複雜:我們只是呼叫 Add 方法 7 次,每次都新增不同的電腦名稱到清單方塊。與其辛苦編輯那些電腦名稱,我們可以從文字檔案讀取那些名稱嗎?當然可以;假設您的電腦名稱在檔案 C:\Scripts\Test.txt 中,您可以將前述 7 行以下列程式碼取代:

Get-Content C:\Scripts\Test.txt | ForEach-Object {[void] $objListBox.Items.Add($_)}

我們在此只是使用 Get-Content 讀取測試檔的內容;然後將那些內容傳送到 ForEach-Object cmdlet,它會為檔案中的每一行 (也就是每一個電腦名稱) 建立新的清單方塊項目。

這完全都「不」複雜。

新增清單方塊到表單 (通常使用 Add 方法) 之後,我們使用下列程式碼,確保當對話方塊顯示在螢幕時,它會顯示在所有其他視窗的上方:

$objForm.Topmost = $True

之後,我們使用這兩行程式碼,讓對話方塊實際顯示在螢幕上:

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

對話方塊顯示在螢幕之後發生什麼事?我們要讓您自行發現究竟發生什麼事。但您應該對發現的結果相當高興。

這就是我們今天要介紹的所有內容。不過,在下一次的專欄中,我們將繼續查看此清單方塊,告訴您一些其他秘訣,例如建立複選清單方塊;預先選取指定的項目... 等等。

大伙兒下週見。

顯示: