使用 Managed 視窗程序將控制項子類別化
更新:2007 年 11 月
.NET Compact Framework 可讓機器碼使用回呼 (Callback) 委派 (Delegate) 呼叫 Managed 程式碼。透過子類別化 (Subclass) Managed 控制項以接收從對應原生控制項的回呼,您就可以建立具備 .NET Compact Framework 中無法直接使用功能的控制項。
對於瞭解 Windows 程式設計和控制項子類別化的程式設計人員而言,這是進階的主題。若要子類別化控制項,您將會需要瞭解原生控制項的內部詳細資料,以及它如何對應至您想要在 Managed 控制項之擴充中提供的功能。您將會需要瞭解要監視哪些 Windows 訊息,以及要叫用 (Invoke) 哪些原生 Windows Embedded CE 函式以提供所需功能。
這個主題將會說明如何子類別化 TreeView 和 Button 控制項,而下列主題則會提供建置應用程式的程式碼範例和指示。
HOW TO 主題 |
說明 |
---|---|
子類別化 TreeView 控制項以建立 NodeMouseClick 事件的實作。.NET Compact Framework 並無法直接支援這個方法,因為必須要限制有限資源的大小。 |
|
子類別化 Button 控制項以顯示色彩豐富的漸層填滿。 請注意,提供這個按鈕主要是要示範如何使用子類別化和回呼。如需建立漸層填滿之按鈕的簡易方式,請參閱 HOW TO:顯示漸層填滿。 |
這些子類別化程式都含有原生 Win32 結構的 WndProcHooker 類別和 Helper 類別、平台叫用宣告,以及 WndProc 委派。如需程式碼清單,請參閱 HOW TO:使用用來攔截 Windows 程序的類別和 HOW TO:使用 Helper 類別進行平台叫用。
WndProcHooker 類別
收到原生控制項的特定 Windows 訊息時,WndProcHooker 類別就可讓該控制項 (或視窗) 叫用 Managed 程式碼的回呼。您可以將原生控制項的視窗程序 (WndProc) 取代成泛型視窗程序 WindowProc 以達成此目的,而且該程序會執行查閱,以判斷控制項是否位於要叫用之回呼方法相關的控制項清單中。如果是的話,該控制項就會視為即將要攔截 (Hook)。
如果控制項已攔截,WindowProc 就會判斷控制項是否對應至回應特定 Windows 訊息。這就是訊息對應 (Message Map),它會將 Windows 訊息與呼叫含有所需功能之 Managed 方法的 WndProcCallback 委派相對應。如果訊息對應含有訊息,則 WndProcCallback 委派就會使用提供給 WindowProc 的訊息參數,叫用程式碼。
攔截控制項
HookWndProc 方法會將控制項的控制代碼與泛型視窗程序 WindowProc 將要使用的訊息對應產生關聯。這就稱為攔截控制項。
HookWndProc 方法會判斷控制項是否已經被攔截。如果沒有,它就會為該控制項建立 HookedProcInformation 物件。這個物件含有控制項和訊息對應的參考。如果已經建立控制項的控制代碼,它就會攔截視窗,方法是建立視窗之原始視窗程序的指標,以供日後還原使用。如果尚未建立控制代碼,它就會被處理 HandleCreated 事件的 ctrl_HandleCreated 方法所攔截。
然後,HookWndProc 方法就會將 HookedProcInformation 物件加入至其中一個泛型字典集合:
hwindDict 字典,其中包含所有已攔截視窗控制代碼的全域清單。索引鍵是 hwnd。已經建立控制代碼的控制項都會進入這個字典。這個字典中的控制項會由任何對應訊息的 WindowProc 進行評估。
ctlDict 字典,其中包含未建立控制代碼的控制項。呼叫 ctrl_HandleCreated 方法時,控制項會移到 hwndDict 字典中。
取消攔截控制項
UnhookWndProc 方法會提供兩種取消攔截控制項的方式:
從控制項的訊息對應移除訊息,但仍然將控制項保留在已攔截視窗的 hwndDict 字典中。此外,這個方法也會使用 HookedProcInformation 物件中保存的指標,還原控制項的原始視窗程序。
從已攔截控制項的 hwndDict 字典移除控制項,然後移除其控制代碼並放置在 ctrlDict 字典中,或是處置整個控制項。此外,這個方法也會使用 HookedProcInformation 物件中保存的控制代碼,還原控制項的原始視窗程序。
子類別化 TreeView 控制項
此範例程式 TreeViewBonus 類別 (列於 HOW TO:使用原生回呼子類別化樹狀檢視控制項中) 會擴充 TreeView 控制項,以便加入無法直接在 .NET Compact Framework 中使用的 NodeMouseClick 事件。
NodeMouseClick 事件的取得方式是將 WM_NOTIFY 訊息加入至控制項的訊息對應,如同 WndProcHooker 類別的執行方式一樣。Managed 回呼方法 WM_Notify_Handler 會叫用原生 GetMessagePos 函式,以取得傳送 Windows 訊息時,滑鼠游標的座標。
請注意,這些座標是相對於螢幕的可見工作區 (Client Area),而非相對於 TreeView 控制項。TreeViewBonus 類別會使用控制項的 PointToClient 方法,將螢幕座標轉換成用戶端座標。然後,就會將這些用戶端座標與 TVM_HITTEST 訊息一併傳送,以判斷是否按下 TreeViewBonus 物件以及按下的位置。
TreeViewBonus 類別含有一些程式碼,可使用原生控制項的 TVM_HITTEST 訊息,取得相對於控制項的座標。
如果點選動作發生在其中一個樹狀檢視節點上,原生 TVHITTESTINFO 結構就會包含該節點的控制代碼。最後一個步驟是由 FindTreeNodeFromHandle 方法,遞迴地周遊的 Managed TreeView 節點,以便找出相符的控制代碼並引發 NodeMouseClick 事件。TreeNodeMouseClickEventArgs 類別可提供下列資料:
已按下的節點
已按下的按鈕。
按下的次數,設定為 1
發生點選動作的 X 座標
發生點選動作的 Y 座標
TreeViewBonus 類別會攔截 Managed 視窗程序之原生樹狀檢視控制項的父代,如同 WndProcHooker 類別的執行方式一樣。然後,它會攔截父控制項,藉以回應 OnParentChanged 事件,進而提供 TreeView 已移至新父代的可能性,例如從 Form 移至 Panel。
子類別化按鈕控制項
GradientFilledButton 和 GradientFill 類別 (列於 HOW TO:使用原生回呼子類別化按鈕控制項中) 會擴充 Button 控制項,以便顯示介於兩個色彩之間的漸層填滿。這個程式主要是用來示範子類別化。不過,在按鈕中顯示漸層填滿較簡單的方式是,建立從 Control 衍生的自訂控制項,如 HOW TO:顯示漸層填滿所述。
GradientFilledButton 類別的建構函式會建立 WndProcHooker 類別的執行個體,以便將 Windows 訊息對應至 Managed 回呼。這些回呼方法會根據 Windows 訊息和控制項的 Capture 屬性狀態,以適當的狀態繪製按鈕。下表將列出對應的 Windows 訊息及其對應回呼。
Windows 訊息 |
Managed 回呼方法和說明 |
---|---|
WM_KEYDOWN |
WM_KeyDown_Handler - 如果 SPACEBAR 或 ENTER (或動作) 鍵已按下,便以壓下的狀態重繪按鈕。 |
WM_KEYUP |
WM_KeyUp_Handler - 以未壓下的狀態重繪按鈕,但如果按下的按鍵是 SPACEBAR 或 ENTER (動作) 鍵,便引發 Click 事件。 |
WM_LBUTTONDOWN |
WM_LeftButtonDown_Hander - 以壓下的狀態重繪按鈕,並將控制項的滑鼠 Capture 屬性設定為 true。 |
WM_LBUTTONUP |
WM_LButtonUp_Handler - 以未壓下的狀態重繪按鈕、如果游標是在控制項的工作區內放開便引發 MouseUp 事件,並將控制項的滑鼠 Capture 屬性設定為 false。 |
WM_MOUSEMOVE |
WM_MouseMove_Handler - 如果之前已按下按鈕且 Capture 為 true,便重繪按鈕。 |
WM_PAINT |
WM_Paint_Handler - 以適當的狀態重繪按鈕。 |
這些 Managed 回呼方法會使用 DrawButton 方法,以適當的狀態繪製按鈕。這個方法含有兩個多載,可在視窗 (本範例所使用) 或 Graphics 物件上繪製按鈕。如果按鈕已按下,則這兩個多載都會採用 true 的布林值 (Boolean)。
GradientFilledButton 類別會使用 GradientFill 類別,執行機器碼的平台叫用呼叫以進行填滿。此外,GradientFill 類別會提供一些屬性,以便設定開始和結束色彩,以及將填滿方向指定為由左至右或由上至下。
請參閱
工作
概念
.NET Compact Framework HOW TO 主題