目录:
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
本文难免有所错误,如有问题欢迎留言