MFC中PreSubclassWindow()什么时候被调用

书籍:《Visual C++ 2017从入门到精通》的4.2 按钮控件

环境:visual studio 2022

内容:[例 4.2]制作图片按钮

说明:以上内容大部分来自腾讯元宝。

在 MFC 中,PreSubclassWindow() 是窗口子类化过程中的关键函数,其调用时机与窗口创建方式密切相关。以下是其调用时机的详细分析:


​1. 窗口子类化的基本流程​

MFC 中窗口的创建和子类化流程如下:

  1. ​窗口句柄创建​​:通过 CreateWindowEx 或 Create 函数创建原生 Windows 窗口。
  2. ​对象关联​​:将窗口句柄(HWND)附加到 MFC 的 CWnd 派生类对象(如 CButton)。
  3. ​子类化处理​​:在此过程中,MFC 调用 PreSubclassWindow(),允许开发者修改窗口属性(如样式、消息处理函数)。

​2. PreSubclassWindow() 的调用时机​

​(1) 通过 CWnd::Create 或 CreateEx 创建窗口​
  • ​调用条件​​:当通过代码直接创建窗口(如 CButton::Create)时,MFC 在窗口附加到 CWnd 对象后立即调用 PreSubclassWindow()
  • ​典型场景​​:
    CMyButton m_btn;
    m_btn.Create(_T("按钮"), WS_CHILD | WS_VISIBLE, CRect(0,0,100,30), this, 1);
    • 此时 PreSubclassWindow() 被调用,允许修改按钮样式(如设置 BS_OWNERDRAW)。
​(2) 通过对话框模板创建控件​
  • ​调用条件​​:当控件通过对话框模板定义,并在运行时通过 DDX_Control 或 SubclassDlgItem 子类化时,PreSubclassWindow() 会被调用。
  • ​典型流程​​:
    1. 对话框初始化时,系统根据模板创建控件窗口。
    2. 对话框的 OnInitDialog 调用 DoDataExchange,触发 DDX_Control
    3. DDX_Control 调用 SubclassDlgItem,此时 PreSubclassWindow() 被执行。
  • ​代码示例​​:
    void CMyDialog::DoDataExchange(CDataExchange* pDX) {
        CDialogEx::DoDataExchange(pDX);
        DDX_Control(pDX, IDC_BUTTON1, m_btn);
    }
​(3) 动态子类化控件​
  • ​调用条件​​:通过 SubclassWindow 或 SubclassDlgItem 动态替换窗口过程时,PreSubclassWindow() 会被调用。
  • ​用途​​:修改控件行为(如拦截消息)或调整样式(如设置透明背景)。

​3. 与其他函数的对比​

​函数​​调用时机​​用途​
PreCreateWindow窗口创建前(调用 CreateEx 时)修改 CREATESTRUCT 参数(如窗口类名、扩展样式)
PreSubclassWindow窗口子类化前(附加到 CWnd 后)修改窗口样式或安装钩子
OnCreate窗口创建后(处理 WM_CREATE初始化控件(如设置字体、定时器)

​关键区别​​:

  • ​对话框控件​​:通过模板创建的控件不会触发 PreCreateWindow,但会触发 PreSubclassWindow
  • ​直接创建控件​​:通过代码调用 Create 时,PreCreateWindow 和 PreSubclassWindow 均会被调用。

​4. 典型应用场景​

​(1) 修改控件样式​

在 PreSubclassWindow 中设置 BS_OWNERDRAW,实现自绘按钮:

void CMyButton::PreSubclassWindow() {
    ModifyStyle(0, BS_OWNERDRAW);  // 启用自绘模式
    CButton::PreSubclassWindow();
}
​(2) 动态调整控件属性​

根据父窗口尺寸调整控件大小:

void CMyButton::PreSubclassWindow() {
    CWnd* pParent = GetParent();
    if (pParent) {
        pParent->GetClientRect(&m_initialRect);
    }
    CButton::PreSubclassWindow();
}
​(3) 拦截消息​

通过修改窗口过程(WndProc)拦截特定消息:

void CMyButton::PreSubclassWindow() {
    WNDPROC oldProc = (WNDPROC)SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, (LONG_PTR)MyCustomProc);
    m_pOldProc = oldProc;  // 保存原窗口过程
    CButton::PreSubclassWindow();
}

​5. 注意事项​

  1. ​调用顺序​​:PreSubclassWindow 在窗口句柄有效后调用,此时可安全操作窗口属性。
  2. ​基类调用​​:若重写此函数,需显式调用基类实现(CWnd::PreSubclassWindow()),否则可能破坏 MFC 的子类化流程。
  3. ​对话框控件​​:对话框中的控件通过模板创建时,PreSubclassWindow 是唯一可修改样式的入口。

​总结​

PreSubclassWindow() 是 MFC 中窗口子类化的核心扩展点,其调用时机集中在以下两种场景:

  1. ​代码直接创建控件​​:通过 Create 或 CreateEx 触发。
  2. ​对话框模板控件​​:通过 DDX_Control 或 SubclassDlgItem 动态子类化时触发。

开发者应优先在此函数中完成样式修改和消息处理,以确保兼容性和灵活性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值