子類別化控件

如果控件幾乎會執行您想要的一切,但您需要更多功能,您可以透過子類別化來變更或新增功能至原始控件。 子類別可以擁有現有類別的所有功能,以及您想要給予它的任何其他功能。

本文件討論如何建立子類別,並包含下列主題。

ComCtl32.dll第 6 版之前的子類別化控件

您可以將控件放在子類別中,並將用戶數據儲存在控件內。 當您使用第 6 版之前的 ComCtl32.dll 版本時,您可以這麼做。 使用舊版的 ComCtl32.dll 建立子類別有一些缺點。

若要建立新的控件,最好從其中一個 Windows 通用控件開始,並加以擴充以符合特定需求。 若要擴充控件,請建立 控件,並將其現有的視窗程式取代為新的視窗程式。 新的程式會攔截控制件的訊息,並對其採取動作,或將它們傳遞至原始程式進行默認處理。 使用 SetWindowLongSetWindowLongPtr 函式來取代 控制元件的 WNDPROC。 下列程式代碼範例示範如何取代 WNDPROC。

OldWndProc = (WNDPROC)SetWindowLongPtr (hButton,
GWLP_WNDPROC, (LONG_PTR)NewWndProc);

儲存用戶數據

您可能想要使用個別視窗來儲存用戶資料。 新的視窗程式可以使用此數據來判斷如何繪製控件或傳送特定訊息的位置。 例如,您可以使用資料來儲存代表控件之類別的 C++ 類別指標。 下列程式代碼範例示範如何使用 SetProp 來儲存具有視窗的數據。

SetProp (hwnd, TEXT("MyData"), (HANDLE)pMyData);

舊子類別化方法的缺點

下列清單指出使用先前所述的子類別化控件方法的一些缺點。

  • 視窗程式只能取代一次。
  • 建立子類別之後,很難移除子類別。
  • 建立私人數據與窗口的關聯效率不佳。
  • 若要在子類別鏈結中呼叫下一個程式,您無法轉換舊的視窗程式並加以呼叫,您必須使用 CallWindowProc 函式來呼叫它。

使用 ComCtl32.dll 第6版的子類別化控件

注意

ComCtl32.dll第 6 版只有 Unicode。 ComCtl32.dll第 6 版所支援的通用控制項不應使用 ANSI 視窗程式進行子類別化(或超類別化)。

 

ComCtl32.dll第 6 版包含四個函式,可讓建立子類別變得更容易,並消除先前討論的缺點。 新的函式會封裝與多個參考數據集相關的管理,因此開發人員可以專注於程序設計功能,而不是管理子類別。 子類別化函式如下:

SetWindowSubclass

此函式一開始用來子類別化視窗。 每個子類別都是由 pfnSubclass 及其 uIdSubclass位址唯一識別。 這兩者都是 SetWindowSubclass 函式的參數 數個子類別可以共用相同的子類別程式,而且標識元可以識別每個呼叫。 若要變更參考數據,您可以對 SetWindowSubclass 進行後續呼叫。 重要的優點是,每個子類別實例都有自己的參考數據。

子類別程式的宣告與一般視窗程式稍有不同,因為它有兩個額外的數據片段:子類別標識符和參考數據。 下列函式宣告的最後兩個參數會顯示這一點。

LRESULT CALLBACK MyWndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass,
DWORD_PTR dwRefData);

每當新視窗程式收到訊息時,就會包含子類別標識符和參考數據。

注意

傳遞至程式的所有字串都是 Unicode 字串,即使 Unicode 未指定為預處理器定義也一樣。

 

下列範例示範子類別化控件之視窗程式的基本架構實作。

LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
                               LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        .
        .
        .
        return TRUE;
    // Other cases...
    } 
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

視窗程式可以附加至對話框程式WM_INITDIALOG處理程式中的 控件,如下列範例所示。

case WM_INITDIALOG:
{
    HWND button = GetDlgItem(hDlg, IDC_OWNERDRAWBUTTON);
    SetWindowSubclass(button, OwnerDrawButtonProc, 0, 0);
    return TRUE;
}

GetWindowSubclass

此函式會擷取子類別的相關信息。 例如,您可以使用 GetWindowSubclass 來存取參考數據。

RemoveWindowSubclass

此函式會移除子類別。 RemoveWindowSubclass 與 SetWindowSubclass 結合,可讓您動態新增和移除子類別。

DefSubclassProc

DefSubclassProc 函式會呼叫子類別鏈結中的下一個處理程式。 函式也會擷取適當的標識碼和參考數據,並將資訊傳遞至下一個窗口程式。