目前寫的 MMC snap-in 已經有點樣子,總算對 MMC snap-in 的程式設計模型有些粗淺的了解,也累積了一些可供日後參考的程式碼。作為上一篇 MMC 筆記的續集,這篇可能會稍嫌雜亂,一方面因為大部分都是隨手記下,加以拼湊;另一方面,最近總覺得時間不太夠用,文章無暇細琢。但如果你是第一次跟 MCM snap-in 打交道,我想裡面有些東西應該會有幫助。當然啦,MSDN 上面的文件最好還是盡量 K,以免遺漏一些關鍵細節,浪費許多摸索和試誤的時間(說我自己啦!)。
MMC Snap-in 的主要元素
先了解 MMC snap-in 的框架和主要元素,寫程式的時候才會比較有方向。參考下圖:
你可以看到,基本上就是把一些管理的項目放在左邊的樹狀結構,當使用者點選樹狀結構中的某個節點(node),中間地帶的 result pane(結果面板)就會顯示該節點所對應的 view。此外,node 和 view 都可能有一些對應的操作,需要讓使用者方便點擊執行,所以右邊的 actions pane 就是用來放這些動作選項。
現在我們知道了,當你要設計一個 MMC snap-in 時,首先要決定哪些東西要擺進左邊的樹狀結構,以及它們的層次要怎麼安排。接著就是當使用者點擊某個 node 時,要以什麼方式呈現,比如說,以 ListView 來顯示其子節點的清單(這是預設的展現方式),或者嵌入一個 Windows Forms 的 user control,或者顯示一個網頁,甚至以 WPF 來呈現。這個中間區域的 view 是很多樣化的,隨你怎麼安排。
Node 與 View
開始寫程式時,至少要先知道兩個類別:ScopeNode 和 ViewDescription。
ScopeNode 可用來建立樹狀結構中的 node 物件。你可以直接使用 ScopeNode 類別,也可以從 ScopeNode 衍生新的子類別,以便加入你需要的屬性和方法。
當使用者點選某個 node 時,就會顯示該 node 所對應的預設 view。既然有預設 view,那就代表一個 node 可以有多種 view 囉?Yes!
在設計 view 的時候,得先知道 MMC 提供哪些類型的 view,共有下列幾種:
比如說,如果你想用 Windows Forms 或 WPF 控制項來設計 UI,就選擇 FormView。選擇 FormView 的意思是寫一個 FormView 的子類別,以便提供額外的屬性或方法。
寫好了你的 FormView 類別,不是直接塞給 ScopeNode 物件,而是得先用一個 ViewDescription 物件將你的 FormView 物件包起來,然後才塞進 ScopeNode 物件的 ViewDescriptions 集合中。如欲指定 node 的預設 view,可設定該 node 物件的 ViewDescriptions.DefaultIndex 屬性。
偷懶一下,程式碼就不貼了,請看這裡:How-To Create a Snap-in That Uses WinForm View。
剛開始練習時,可以直接使用現成的 ScopeNode 類別來建立樹狀節點,而且不去管它要用什麼 View。先觀察這種陽春設計的 UI 會是什麼樣子,然後,還是用現成的 ScopeNode,但這次加入自訂的 View 類別。讓節點跟某個自訂 view 綁在一起。觀察執行結果,試改一些 code,以確實了解 ScopeNode 跟 View 的關係。
接著設計不同的 views,讓一個節點有多種 view 可供使用者切換。這個部分可以參考微軟網站上的教學文件:How-To Develop Snap-ins Using MMC。你可以先試試 MMCLsitView,然後 FormView。
然後,你可能會發現,node 和 view 之間必須要能互相傳遞資料,至少 view 物件要能夠取得 node 物件的某些資訊。比如說,每個節點代表一台機器,它得提供 IP 位址、電腦名稱等資訊。於是你寫了一個類別,繼承自 ScopeNode,例如 ServerScopeNode,並且在此類別中提供額外的屬性:IPAddress、ComputerName 等等。於是,在你的 view 類別裡,便可透過 Node 屬性來取得與之關聯的節點物件,將它手動轉型為 ServerScopeNode,便可以取得你先前存入 node 物件的額外屬性。(謎之音:沒 sample code 誰看得懂這段繞口令在說啥啊!)
底下是一些實作時碰到的問題或值得注意的地方,大多和 FormView 有關。
Microsoft.ManagementConsole 命名空間裡面的類別 Action 跟 System 命名空間的 Action 類別名稱相同,為了避免名稱衝突,又不用寫一長串的類別全名,我在程式碼的上方加入兩行 using 敘述:
using Microsoft.ManagementConsole;
using MMC=Microsoft.ManagementConsole;
這樣的話,碰到名稱衝突時,就用 MMC.* 來指明使用 Microsoft.ManagementConsole 命名空間裡的類別。例如:MMC.Action。
多執行緒的注意事項
MMC 是 MDI 應用程式,因此儘管使用者同時間只操作或看到其中一個 view,但其實背後可能已經同時開啟多個 view。如果這些 view 物件可能又有額外建立多個工作執行緒,那麼同時間可能會有很多執行緒在運行。
在我的 MMC snap-in 中,我同時使用了 System.Windows.Forms.Timer 和 System.Timers.Timer 來分別負責 UI 更新以及背景資料讀取的工作。如果使用者開了很多個 view,背後會有好多執行緒在跑。
對於這種多執行緒的場合,就得特別注意減輕不必要的負荷,並且確實做好資源回收的工作。當使用者關閉 MMC snap-in 時,要先把所有的工作執行緒結束掉,否則 MMC 可能會出現錯誤訊息或丟出 ThreadAbortException(執行緒被強迫終止)。
如何得知 FormView 已不可見
當使用者從節點 A 的 view 切換到另一個節點 B 或其他 view 之後,節點 A 裡面的執行緒其實還在背後持續運作。為了避免無謂的 UI 更新或減少執行緒的處理負擔,我們會需要偵測目前的 FormView 物件是否仍為可見。
作法很簡單:改寫 FormView 的 OnHide 和 OnShow 虛擬方法,並在其中設定其內含之使用者控制項的 Visible 屬性。例如:
使用者控制項如需在 Visible 屬性變更時立即做出對應處置,則可訂閱自己的 VisibleChanged 事件。
如何以程式碼點選特定 ScopeNode
View 物件有提供 SelectScopeNode 方法,讓你用程式碼來將目前的焦點切到指定的 node,就像使用者用滑鼠點選某個節點的效果。
以 FormView 來說,實際的 UI 控制項都是放在一個 user control 裡面,可是 user control 就只是一般的 UserControl 子類別,它沒有 SelectScopeNode 方法,所以如果想要讓 user control 中的某個按鈕被按下去時切換到特定的 node,我的作法是在我的 user control 類別中 expose 一個事件,例如:
public event Action<string> Button1Clicked;
然後,在我的自訂 FormView 類別中訂閱 user control 的 Button1Clicked 事件(FormView 類別有個 Control 屬性可取得嵌入其中的控制項),例如:
小結
用來寫一些管理類型的工具,MMC snap-in 真是挺好用的。這篇筆記還有不少東西沒提到,例如 property page、action 等。
先這樣吧!日後有空再回頭補充。
MMC Snap-in 的主要元素
先了解 MMC snap-in 的框架和主要元素,寫程式的時候才會比較有方向。參考下圖:
你可以看到,基本上就是把一些管理的項目放在左邊的樹狀結構,當使用者點選樹狀結構中的某個節點(node),中間地帶的 result pane(結果面板)就會顯示該節點所對應的 view。此外,node 和 view 都可能有一些對應的操作,需要讓使用者方便點擊執行,所以右邊的 actions pane 就是用來放這些動作選項。
現在我們知道了,當你要設計一個 MMC snap-in 時,首先要決定哪些東西要擺進左邊的樹狀結構,以及它們的層次要怎麼安排。接著就是當使用者點擊某個 node 時,要以什麼方式呈現,比如說,以 ListView 來顯示其子節點的清單(這是預設的展現方式),或者嵌入一個 Windows Forms 的 user control,或者顯示一個網頁,甚至以 WPF 來呈現。這個中間區域的 view 是很多樣化的,隨你怎麼安排。
Node 與 View
ScopeNode 可用來建立樹狀結構中的 node 物件。你可以直接使用 ScopeNode 類別,也可以從 ScopeNode 衍生新的子類別,以便加入你需要的屬性和方法。
當使用者點選某個 node 時,就會顯示該 node 所對應的預設 view。既然有預設 view,那就代表一個 node 可以有多種 view 囉?Yes!
在設計 view 的時候,得先知道 MMC 提供哪些類型的 view,共有下列幾種:
- MmcListView:可在 result pane 中顯示一堆項目清單。
- FormView:可讓你在 result pane 中嵌入 Windows Forms 或 WPF 控制項。
- HtmlView:可嵌入一個網頁。
- MessageView:用來顯示標準的 MMC 訊息。
比如說,如果你想用 Windows Forms 或 WPF 控制項來設計 UI,就選擇 FormView。選擇 FormView 的意思是寫一個 FormView 的子類別,以便提供額外的屬性或方法。
寫好了你的 FormView 類別,不是直接塞給 ScopeNode 物件,而是得先用一個 ViewDescription 物件將你的 FormView 物件包起來,然後才塞進 ScopeNode 物件的 ViewDescriptions 集合中。如欲指定 node 的預設 view,可設定該 node 物件的 ViewDescriptions.DefaultIndex 屬性。
偷懶一下,程式碼就不貼了,請看這裡:How-To Create a Snap-in That Uses WinForm View。
剛開始練習時,可以直接使用現成的 ScopeNode 類別來建立樹狀節點,而且不去管它要用什麼 View。先觀察這種陽春設計的 UI 會是什麼樣子,然後,還是用現成的 ScopeNode,但這次加入自訂的 View 類別。讓節點跟某個自訂 view 綁在一起。觀察執行結果,試改一些 code,以確實了解 ScopeNode 跟 View 的關係。
接著設計不同的 views,讓一個節點有多種 view 可供使用者切換。這個部分可以參考微軟網站上的教學文件:How-To Develop Snap-ins Using MMC。你可以先試試 MMCLsitView,然後 FormView。
然後,你可能會發現,node 和 view 之間必須要能互相傳遞資料,至少 view 物件要能夠取得 node 物件的某些資訊。比如說,每個節點代表一台機器,它得提供 IP 位址、電腦名稱等資訊。於是你寫了一個類別,繼承自 ScopeNode,例如 ServerScopeNode,並且在此類別中提供額外的屬性:IPAddress、ComputerName 等等。於是,在你的 view 類別裡,便可透過 Node 屬性來取得與之關聯的節點物件,將它手動轉型為 ServerScopeNode,便可以取得你先前存入 node 物件的額外屬性。(謎之音:沒 sample code 誰看得懂這段繞口令在說啥啊!)
底下是一些實作時碰到的問題或值得注意的地方,大多和 FormView 有關。
FormView 的初始化過程
在使用 FormView (或繼承自 FormView 的自訂類別)時,我們會設計一個自訂的 user control 來嵌入這個 form。當使用者透過 MMC 管理介面開啟這個 view 時,會先觸發我們的 user control 的 Load 事件,然後才會呼叫 FormView 的 OnInitialize 方法。
進一步說,user control 的初始化過程如下:
進一步說,user control 的初始化過程如下:
- 執行 user control 的類別建構子。
- 執行 user control 的 Initialize 方法。此方法定義於 Microsoft.ManagementConsole.IFormViewControl。我們的 user control 除了繼承 UserControl 類別,還要實作這個 IFormControl 介面。實作該介面的 Initialize 方法,我們就可以從該方法的 View 參數來取得此 user control 外層的 FormView 物件。然後,如果需要的話,又可以透過該 FormView 物件取得其關聯的 Node 物件。
- 觸發 Load 事件。
類別名稱衝突
using Microsoft.ManagementConsole;
using MMC=Microsoft.ManagementConsole;
這樣的話,碰到名稱衝突時,就用 MMC.* 來指明使用 Microsoft.ManagementConsole 命名空間裡的類別。例如:MMC.Action。
多執行緒的注意事項
MMC 是 MDI 應用程式,因此儘管使用者同時間只操作或看到其中一個 view,但其實背後可能已經同時開啟多個 view。如果這些 view 物件可能又有額外建立多個工作執行緒,那麼同時間可能會有很多執行緒在運行。
在我的 MMC snap-in 中,我同時使用了 System.Windows.Forms.Timer 和 System.Timers.Timer 來分別負責 UI 更新以及背景資料讀取的工作。如果使用者開了很多個 view,背後會有好多執行緒在跑。
對於這種多執行緒的場合,就得特別注意減輕不必要的負荷,並且確實做好資源回收的工作。當使用者關閉 MMC snap-in 時,要先把所有的工作執行緒結束掉,否則 MMC 可能會出現錯誤訊息或丟出 ThreadAbortException(執行緒被強迫終止)。
如何得知 FormView 已不可見
當使用者從節點 A 的 view 切換到另一個節點 B 或其他 view 之後,節點 A 裡面的執行緒其實還在背後持續運作。為了避免無謂的 UI 更新或減少執行緒的處理負擔,我們會需要偵測目前的 FormView 物件是否仍為可見。
作法很簡單:改寫 FormView 的 OnHide 和 OnShow 虛擬方法,並在其中設定其內含之使用者控制項的 Visible 屬性。例如:
protected override void OnHide() { base.OnHide(); this.Visible = false; } protected override void OnShow() { base.OnShow(); this.Visible = true; }
使用者控制項如需在 Visible 屬性變更時立即做出對應處置,則可訂閱自己的 VisibleChanged 事件。
如何以程式碼點選特定 ScopeNode
View 物件有提供 SelectScopeNode 方法,讓你用程式碼來將目前的焦點切到指定的 node,就像使用者用滑鼠點選某個節點的效果。
以 FormView 來說,實際的 UI 控制項都是放在一個 user control 裡面,可是 user control 就只是一般的 UserControl 子類別,它沒有 SelectScopeNode 方法,所以如果想要讓 user control 中的某個按鈕被按下去時切換到特定的 node,我的作法是在我的 user control 類別中 expose 一個事件,例如:
public event Action<string> Button1Clicked;
然後,在我的自訂 FormView 類別中訂閱 user control 的 Button1Clicked 事件(FormView 類別有個 Control 屬性可取得嵌入其中的控制項),例如:
public class SelectionFormView : FormView { private MyUserControl myControl = null; .... protected override void OnInitialize(AsyncStatus status) { base.OnInitialize(status); // Get a typed reference to the hosted control // that is set up by the form view description. myControl = (MyUserControl)this.Control; myControl += new Action<string>(ControlButtonClicked) Refresh(); } void ControlButtonClicked(string nodeName) { // 這裡用比較"純樸"的方法尋找樹狀結構中的特定節點. ScopeNode parentNode = this.ScopeNode.Children[0]; for (int i = 0; i < Node.Children.Count; i++ ) { MyScopeNode aNode = parentNode.Children[i] as MyScopeNode; if (aNode != null) { if (aNode.Name.Equals(nodeName)) { this.SelectScopeNode(aNode); } } } } }
小結
用來寫一些管理類型的工具,MMC snap-in 真是挺好用的。這篇筆記還有不少東西沒提到,例如 property page、action 等。
先這樣吧!日後有空再回頭補充。
延伸閱讀
沒有留言: