如何在MFC的CTreeList表头添加ComboBox并捕获其事件
在MFC中捕获CTreeListHeader内ComboBox的事件
你遇到的问题核心是:ComboBox创建在CHeaderCtrl(CTreeList的表头)下时,其通知消息会发送给父窗口CHeaderCtrl,而非对话框,因此对话框的ON_CBN_SELCHANGE宏无法捕获事件。以下是两种可行的解决方法:
方法一:子类化CHeaderCtrl转发消息
通过自定义CHeaderCtrl子类,拦截ComboBox的通知消息并转发给对话框:
- 创建CHeaderCtrl子类
class CMyHeaderCtrl : public CHeaderCtrl { DECLARE_MESSAGE_MAP() public: afx_msg void OnCommand(WPARAM wParam, LPARAM lParam); }; BEGIN_MESSAGE_MAP(CMyHeaderCtrl, CHeaderCtrl) ON_WM_COMMAND() END_MESSAGE_MAP() void CMyHeaderCtrl::OnCommand(WPARAM wParam, LPARAM lParam) { UINT ctrlID = LOWORD(wParam); UINT notifyCode = HIWORD(wParam); // 匹配目标ComboBox的ID和选择变化通知 if (ctrlID == IDC_CMB && notifyCode == CBN_SELCHANGE) { // 获取对话框窗口(CTreeList的父窗口) CWnd* pDlg = GetParent()->GetParent(); if (pDlg) { // 将消息转发给对话框 pDlg->SendMessage(WM_COMMAND, wParam, lParam); } } // 调用基类处理其他命令 CHeaderCtrl::OnCommand(wParam, lParam); }
- 在对话框中替换表头控件
在对话框的OnInitDialog中,将CTreeList的默认表头替换为自定义子类:
BOOL CMFCtestDlg::OnInitDialog() { CMjAcDialog::OnInitDialog(); RECT rect; m_cTree.InsertColumn(0, L"xxx", 0, 80); m_cTree.InsertColumn(1, L"Popis", 0, 180); // 子类化表头控件 static CMyHeaderCtrl m_myHeader; HWND hHeader = m_cTree.GetHeaderCtrl()->m_hWnd; m_myHeader.SubclassWindow(hHeader); CHeaderCtrl* pHead = &m_myHeader; // 后续创建ComboBox的代码保持不变 if (pHead != NULL) { pHead->GetItemRect(0, &rect); m_pCombo = new CComboBox; RECT cmbRect; cmbRect.left = rect.left + 1; cmbRect.top = rect.top + 1; cmbRect.right = rect.right - 1; cmbRect.bottom = rect.top + 14; m_pCombo->Create(CBS_DROPDOWN | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, cmbRect, pHead, IDC_CMB); // 调整表头高度等代码保持不变 m_pCombo->GetWindowRect(&cmbRect); RECT rectHeader; pHead->GetClientRect(&rectHeader); pHead->MoveWindow(rectHeader.left, rectHeader.top, (rectHeader.right - rectHeader.left), cmbRect.bottom - cmbRect.top + 2, TRUE); pHead->Invalidate(); } // 填充ComboBox、插入树节点的代码保持不变 return TRUE; }
- 对话框中正常处理事件
对话框的消息映射和事件处理函数可以直接使用原有代码:
BEGIN_MESSAGE_MAP(CMFCtestDlg, CMjAcDialog) ON_CBN_SELCHANGE(IDC_CMB, &CMFCtestDlg::OnCbnSelChangeCmb) END_MESSAGE_MAP() void CMFCtestDlg::OnCbnSelChangeCmb() { int nSel = m_pCombo->GetCurSel(); CString strSelText; if (nSel != CB_ERR) { m_pCombo->GetLBText(nSel, strSelText); // 在这里添加你的业务逻辑 } }
方法二:将ComboBox的父窗口改为对话框
直接让ComboBox的父窗口是对话框,而非表头控件,这样通知消息会直接发送给对话框:
修改OnInitDialog中创建ComboBox的代码,调整坐标计算和父窗口参数:
BOOL CMFCtestDlg::OnInitDialog() { CMjAcDialog::OnInitDialog(); RECT rect; m_cTree.InsertColumn(0, L"xxx", 0, 80); m_cTree.InsertColumn(1, L"Popis", 0, 180); CHeaderCtrl* pHead = m_cTree.GetHeaderCtrl(); pHead->GetItemRect(0, &rect); if (pHead != NULL) { m_pCombo = new CComboBox; // 将表头内的坐标转换为对话框客户区坐标 pHead->ClientToScreen(&rect); this->ScreenToClient(&rect); RECT cmbRect; cmbRect.left = rect.left + 1; cmbRect.top = rect.top + 1; cmbRect.right = rect.right - 1; cmbRect.bottom = rect.top + 14; // 将父窗口设为对话框(this) m_pCombo->Create(CBS_DROPDOWN | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, cmbRect, this, IDC_CMB); // 调整表头高度的代码保持不变 m_pCombo->GetWindowRect(&cmbRect); RECT rectHeader; pHead->GetClientRect(&rectHeader); pHead->MoveWindow(rectHeader.left, rectHeader.top, (rectHeader.right - rectHeader.left), cmbRect.bottom - cmbRect.top + 2, TRUE); pHead->Invalidate(); } // 填充ComboBox、插入树节点的代码保持不变 return TRUE; }
此时对话框原有的ON_CBN_SELCHANGE(IDC_CMB, &CMFCtestDlg::OnCbnSelChangeCmb)宏即可正常捕获事件,无需额外修改。
内容的提问来源于stack exchange,提问作者Michal Hadraba
相关产品推荐
相关产品推荐

