窗口子類化

目錄:
1、Win32中實現窗口子類化

  • SetWindowSubclass
  • SetWindowLong
  • 示例

2、MFC中實現子類化

  • SubclassDlgItem
  • DDX_Control
  • SubclassWindow

Win32中實現窗口子類化

方法一:
SetWindowSubclass

BOOL SetWindowSubclass(
  _In_  HWND hWnd,
  _In_  SUBCLASSPROC pfnSubclass,
  _In_  UINT_PTR uIdSubclass,
  _In_  DWORD_PTR dwRefData
);

功能:設置子窗口的回調函數
hWnd:進行子類化的窗口句柄
pfnSubclass:子窗口的回調函數
uIdSubclass:子窗口ID,隨便你設置
dwRefData:傳遞給窗口回調函數的參數,隨便你傳遞
RemoveWindowSubclass

BOOL RemoveWindowSubclass(
  _In_  HWND hWnd,
  _In_  SUBCLASSPROC pfnSubclass,
  _In_  UINT_PTR uIdSubclass
);

功能:移除子類化窗口的回調函數
DefSubclassProc

LRESULT DefSubclassProc(
  _In_  HWND hWnd,
  _In_  UINT uMsg,
  _In_  WPARAM WPARAM,
  _In_  LPARAM LPARAM
);

功能:子類化窗口的默認消息處理函數
GetWindowSubclass

BOOL GetWindowSubclass(
  _In_   HWND hWnd,
  _In_   SUBCLASSPROC pfnSubclass,
  _In_   UINT_PTR uIdSubclass,
  _Out_  DWORD_PTR *pdwRefData
);

功能:獲取子窗口的參數
pdwRefData:輸出型參數
回調函數原型

typedef LRESULT ( CALLBACK *SUBCLASSPROC)(
  HWND hWnd,
  UINT uMsg,
  WPARAM wParam,
  LPARAM lParam,
  UINT_PTR uIdSubclass,
  DWORD_PTR dwRefData
);

uIdSubclass:這就是SetWindowSubclass第三個參數傳遞的ID值
dwRefData:這就是SetWindowSubclass第四個參數傳遞的值


方法二:
SetWindowLong

LONG WINAPI SetWindowLong(
  _In_  HWND hWnd,
  _In_  int nIndex,
  _In_  LONG dwNewLong
);

功能:設置子窗口的消息回調函數的新地址,通俗來說就是攔截該空間消息到指定的消息回調函數處理(關於nIndex的參數設置有很多,這裏僅使用子類化GWL_WNDPROC參數)
hWnd:進行操作的窗口句柄
nIndex:這裏使用 GWL_WNDPROC
dwNewLong:回調函數指針
回調函數原型

LRESULT APIENTRY SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

示例:

#include <windows.h>
#include <tchar.h>
#include "resource.h"
#include <string>
using namespace std;

#include <Commctrl.h>
#pragma comment(lib,"Comctl32.lib")

#define SUB_BTN_ID  0x11            

HINSTANCE m_hInstance;              // 實例
HWND m_hWnd;                        // 主窗口句柄
WNDPROC g_EditProc;                 // 回調函數指針

enum {
    USER_EDIT_CODE = WM_USER + 100, // 自定義消息
};

INT_PTR CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);                            // 主窗口回調
LRESULT CALLBACK BtnSubclassProc(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR);  // 按鈕子類化回調 
LRESULT APIENTRY EditSubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); // Edit子類化回調

int WINAPI _tWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPTSTR lpCmdLine,
    _In_ int nShowCmd)
{
    m_hInstance = hInstance;
    DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,DialogProc);

    _CrtDumpMemoryLeaks();
    return 0;
}

INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == USER_EDIT_CODE)
    {
        return true;
    }

    switch (uMsg)
    {
    case WM_INITDIALOG:
    {
        m_hWnd = hwndDlg;
        SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_ICON)));
        SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_ICON)));

        HFONT hFont = CreateFont(-14/*高*/, -7/*寬*/, 0, 0, 700 /*700表示粗體*/,
            FALSE/*斜體?*/, FALSE/*下劃線?*/, FALSE/*刪除線?*/, DEFAULT_CHARSET,
            OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
            FF_DONTCARE, TEXT("微軟雅黑")
        );
        SendMessage(GetDlgItem(hwndDlg, IDC_EDIT), WM_SETFONT, (WPARAM)hFont, NULL);
        SendMessage(GetDlgItem(hwndDlg, IDC_BTN_TEST), WM_SETFONT, (WPARAM)hFont, NULL);

        // SetWindowLong 返回 子類化回調函數進行默認消息處理 的函數指針
        g_EditProc = (WNDPROC)SetWindowLong(
            GetDlgItem(hwndDlg, IDC_EDIT),  // 進行子類化的窗口句柄
            GWL_WNDPROC,                    // 窗口回調(參數)
            (LONG)EditSubclassProc);        // 窗口回調函數

        if (!SetWindowSubclass(
            GetDlgItem(hwndDlg, IDC_BTN_TEST),// 子類化的窗口句柄
            BtnSubclassProc,            // 子類化的回調函數
            SUB_BTN_ID,             // 被子類化的控件 ID,便於回調函數中判斷是哪個窗口的消息
            (DWORD_PTR)hwndDlg)     // 傳遞給子控件的參數
            )
        {
            return FALSE;
        }

        break;
    }

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
            case IDCANCEL:
            {
                RemoveWindowSubclass(GetDlgItem(hwndDlg, IDC_BTN_TEST), BtnSubclassProc, SUB_BTN_ID);
                SetWindowLong(GetDlgItem(hwndDlg, IDC_EDIT), GWL_WNDPROC, (LONG)g_EditProc);
                EndDialog(hwndDlg, uMsg);
                break; 
            }
        }
        break;
    }

    default:
        break;
    }

    return (INT_PTR)FALSE;
}

LRESULT CALLBACK BtnSubclassProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,
    UINT_PTR uIdSubclass,DWORD_PTR dwRefData)
{
    if (uIdSubclass == SUB_BTN_ID)
    {
        switch (uMsg)
        {
            case WM_LBUTTONDOWN:
            {
                wstring txt = _T("這是子類化控件消息");
                SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)txt.c_str());
                return TRUE;
            }
        }
    }

    // 調用窗口的子類鏈中的下一個處理程序。子類鏈中的最後一個處理程序調用窗口的原始窗口過程。
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

LRESULT APIENTRY EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_RBUTTONDOWN:
        {
            ::SetWindowText(hwnd, _T("右鍵點擊"));
            SendMessage(hwnd, EM_SETSEL, 0, -1);
            break;
        }
        case WM_CHAR:
        {
            char ascii_code = wParam;
            if (ascii_code >= 'A' && ascii_code <= 'Z' || ascii_code >= 'a' && ascii_code <= 'z')
            {
                SendMessage(m_hWnd, USER_EDIT_CODE, 0, 0);
                return true;
            }
            break;
        }
    }

    return CallWindowProc(g_EditProc, hwnd, uMsg, wParam, lParam);
    //return g_EditProc(hwnd, uMsg, wParam, lParam);
}

MFC中實現子類化

子類化的實現:我們在一個窗口(指MFC中的窗口,不是其它的界面庫)之中,子窗口其實也是接受到消息了的,但是我們卻無法處理,此時我們就需要子類化了。子類化其實就是對子窗口(控件也是一個窗口)實現一個消息回調函數(在MFC中就是實現一個消息處理的類,該類實現消息映射),用於處理我們想要的子窗口的消息。
注:MFC中,窗口是覆蓋型的,也就是在主窗口的某個區域實現了一個子窗口時,此時該區域接收到消息的是子窗口,而不是主窗口。

方法一
BOOL SubclassDlgItem(UINT nID, CWnd* pParent);
註釋:該函數沒有子函數,不需要釋放
示例:
m_editName.SubclassDlgItem(IDC_NAME, this);

方法二:
DDX_Control宏實現子類化,也就是通過在控件上右鍵點擊,添加關聯變量,此時就是通過該宏實現的。實現子類化的一些消息攔截的時候,只需要右鍵類嚮導添加自己想要處理的控件消息就行(注:不要添加成主窗口的消息)

方法三:
BOOL SubclassWindow( HWND hWnd );
HWND UnsubclassWindow( );
註釋:使用SubclassWindow進行子類化之後,在釋放的的時候,要進行UnsubclassWindow反子類化

MSDN示例:

HBRUSH CSuperComboBox::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
   if (nCtlColor == CTLCOLOR_EDIT)
   {
      //Edit control 
      if (m_edit.GetSafeHwnd() == NULL)
         m_edit.SubclassWindow(pWnd->GetSafeHwnd());
   }
   else if (nCtlColor == CTLCOLOR_LISTBOX)
   {
      //ListBox control 
      if (m_listbox.GetSafeHwnd() == NULL)
         m_listbox.SubclassWindow(pWnd->GetSafeHwnd());
   }

   HBRUSH hbr = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
   return hbr;
}

void CSuperComboBox::OnDestroy()
{
   //unsubclass edit and list box before destruction 
   if (m_edit.GetSafeHwnd() != NULL)
      m_edit.UnsubclassWindow();
   if (m_listbox.GetSafeHwnd() != NULL)
      m_listbox.UnsubclassWindow();
   CComboBox::OnDestroy();
}

關於控件關聯的 Attach Detach 的記錄
BOOL Attach( HWND hWndNew );
HWND Detach( );
解釋:Attach使得變量與控件相關聯,我們通過相關聯的變量就可以實現對控件的操作。在退出程序的時候,要進行對關聯進行釋放(MSDN:分離 CWnd 對象的一個Windows句柄並返回處理)。

m_list.Attach(::GetDlgItem(GetSafeHwnd(), IDC_LIST));

m_list.SetBkColor(RGB(220, 255, 255));//深青色 淺青色
m_list.SetTextBkColor(RGB(220, 255, 255));
m_list.SetTextColor(RGB(0, 0, 255));
m_list.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);

m_list.InsertColumn(0, _T("工號"), 0, 120);
m_list.InsertColumn(1, _T("姓名"), 0, 160);
m_list.InsertColumn(2, _T("工資"), 0, 160);
m_list.InsertColumn(3, _T("入職日期"), 0, 160);

m_list.Detach();

示例代碼:https://pan.baidu.com/s/1o7YE0Hk
本文難免有所錯誤,如有問題歡迎留言

發佈了54 篇原創文章 · 獲贊 38 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章