目錄:
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
本文難免有所錯誤,如有問題歡迎留言