窗口子类化

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值