控件的通知消息被送到哪里去了?

fangxu1999 2008-03-26 12:05:49
我从CComboBox类派生出一个自定义的类CMyCombobox,主要用来实现根据编辑框的内容进行筛选列表框中的数据项的功能,例如:在创建时将所有可能的数据都存进去,当编辑框中输入A,则列表框中将所有以A打头的都列出来。
使用了ON_CONTROL_REFLECT(CBN_EDITCHANGE, OnEditchange)在列表框中进行了处理,此时当Edit内容改变时,列表框能够相应该消息。
我把这个自定义的组合框放到了对话框中,然后通过一个Button向组合框发送CBN_EDITCHANGE,发现无论是组合框还是对话框都不响应这个消息,不知道消息究竟被送到哪里处理了。

//对话框中按钮发消息
void CMyDlg::OnButton1()
{
SendDlgItemMessage(IDC_COMBO1, CBN_EDITCHANGE, IDC_COMBO1, (LPARAM)(GetDlgItem(IDC_COMBO1)->GetSafeHwnd()));
}

//对话框来处理消息
ON_CBN_EDITCHANGE(IDC_COMBO1, OnEditchangeCombo1)
void CMyDlg::OnEditchangeCombo1()
{
AfxMessageBox("CDlgTestDlg::OnEditchangeCombo1");
}

//组合框本身来处理消息
ON_CONTROL_REFLECT(CBN_EDITCHANGE, OnEditchange)
void CMyCombobox::OnEditchange()
{
AfxMessageBox("OnEditchange");
}

如果使用SendDlgItemMessage(.., WM_CHAR, ..)之类的Windows消息,测试发现是由组合框本身处理的,即使对话框也提供了处理方法,这是由于消息被发往了组合框窗口过程,很容易理解。可是如果是发送的控件通知消息,虽然是向控件发的,但是通知消息本身又是通知其父窗口的,父窗口又会反射消息...,确实搞糊涂了,请问一下控件的通知消息到底是怎么路由传递的?它和命令消息WM_COMMAND有什么区别啊?MSDN上好像说是实际上就是发的WM_COMMAND消息。而命令消息就会直接调用OnCommand进行处理了,应该不会向外发的。针对这里的情况,能不能有一个好的说明,到底CBN_EDITCHANGE被送到哪里去了啊?
谢谢指点。
...全文
311 点赞 收藏 20
写回复
20 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
wwwhhb4002 2009-03-03

学习
回复
吹雪 2008-04-02

BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT* pResult)
{
// get the map, and if no map, then this message does not need reflection
CHandleMap* pMap = afxMapHWND();
if (pMap == NULL)
return FALSE;

// check if in permanent map, if it is reflect it (could be OLE control)
CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild);
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWndChild);
if (pWnd == NULL)
{
#ifndef _AFX_NO_OCC_SUPPORT
// check if the window is an OLE control
CWnd* pWndParent = (CWnd*)pMap->LookupPermanent(::GetParent(hWndChild));
if (pWndParent != NULL && pWndParent->m_pCtrlCont != NULL)
{
// If a matching control site exists, it's an OLE control
COleControlSite* pSite = (COleControlSite*)pWndParent->
m_pCtrlCont->m_siteMap.GetValueAt(hWndChild);
if (pSite != NULL)
{
CWnd wndTemp(hWndChild);
wndTemp.m_pCtrlSite = pSite;
LRESULT lResult = wndTemp.SendChildNotifyLastMsg(pResult);
wndTemp.m_hWnd = NULL;
return lResult;
}
}
#endif //!_AFX_NO_OCC_SUPPORT
return FALSE;
}

// only OLE controls and permanent windows will get reflected msgs
ASSERT(pWnd != NULL);
return pWnd->SendChildNotifyLastMsg(pResult);
}

回复
zaodt 2008-03-26

楼主有空可以看看这本书:

『Windows程序设计』,下载地址:

http://blog.csdn.net/zaodt/archive/2007/11/25/1901332.aspx
回复
zaodt 2008-03-26

MSDN:

The CBN_EDITCHANGE notification message is sent after the user has taken an action that may have altered the text in the edit control portion of a combo box.


The parent window of the combo box receives this notification message through the WM_COMMAND message.


当用户改变编辑框中的内容时,控件的父窗口会收到【CBN_EDITCHANGE】消息;

父窗口的回调函数会响应【WM_COMMAND】进行处理。
回复
avcoder 2008-03-26
1楼的正确。
回复
yxz_lp 2008-03-26
SendMessage(WM_COMMAND , MAKEWPARAM(IDC_COMBO1,CBN_EDITCHANGE), (LPARAM)(GetDlgItem(IDC_COMBO1)->GetSafeHwnd()));
回复
cnzdgs 2008-03-26
你按下面的方式处理,不需要Button。
class CMyComboBox : public CComboBox
{
DECLARE_DYNAMIC(CMyComboBox)
protected:
DECLARE_MESSAGE_MAP()
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
};

IMPLEMENT_DYNAMIC(CMyComboBox, CComboBox)

BEGIN_MESSAGE_MAP(CMyComboBox, CComboBox)
END_MESSAGE_MAP()

BOOL CMyComboBox::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (HIWORD(wParam) == EN_CHANGE)
MessageBox(_T("OK"));
return CComboBox::OnCommand(wParam, lParam);
}
回复
fangxu1999 2008-03-26
确实如上面那样,我们也可以使用CWnd::SendDlgItemMessage(id, msg, wparam,lparam)直接向Combobox发CBN_EDITCHANGE了,虽然道理上有点说不通。

在父窗口的OnCommand中,会首先调用子控件的OnChildNotify方法,如果在此实现了对某些消息的处理工作,并返回TRUE即可。如果没有处理,则会调用默认的控件所继承的类的OnChildNotify方法,这里当然是CCombobox::OnChildNotify方法。这个父类的方法有一些通用的默认处理操作,还可以根据在消息映射表中所注册的条目进行查找,处理我们已经给出映射的消息。如果这样都还找不到处理的该消息的办法,那么就回到父窗口的OnCommand,在父窗口中进行通常的处理了。

参考了http://blog.csdn.net/txdog/archive/2007/08/18/1749841.aspx 只是没有看到CWnd::OnNotify的使用,因为根本进不去. OnCommand->OnChildNotify->ON_NOTIFY_REFLECT->OnCommand。而使用新的控件(如Datatimepicker)就可以收到WM_NOTIFY消息,但此时却又收不到WM_COMMAND了,只能调用CWnd::OnNotify。

调了很久,终于有点眉目,可以小节了。如果控件自己需要处理消息,需要改写控件的OnChildNotify或是ON_NOTIFY_REFLECT提供通知反射处理。
之前我主要出现的问题就在于对通知消息的实质没有掌握清楚,CBN_EDITCHANGE仅仅是一个通知码,需要作为其中的WPARAM的一部分进行发送,真正的消息应该是WM_COMMAND。

还请高手解释一下问什么消息从 源自于Edit的EN_CHANGE通知消息给Combobox 变成了 源自于Combobox的CBN_EDITCHANGE通知消息给CDialog的了?(调试的结果见16楼)

谢谢关注。
回复
fangxu1999 2008-03-26
[Quote=引用 14 楼 cnzdgs 的回复:]
你这种测试的想法是错的,ComboBox的内容改变是由它来通知其父窗口的,而不是由外部控件来通知它。
ComboBox内部包含了一个EDIT控件,EDIT控件的内容改变时会发消息给ComboBox,你在派生的ComboBox中响应EN_CHANGE消息试试。
[/Quote]


按道理确实需要如此处理,通知消息是通知外面父窗口的。可是不晓得::SendMessage怎么搞的,我发源自于Edit的EN_CHANGE通知消息给Combobox,通过系统API ::SendMessage发过去,在User32中不晓得怎么转悠的,当到了AfxWndProcBase的时候竟然变成了源自于Combobox的CBN_EDITCHANGE通知消息给CDialog了。具体结果如下:
在CDialog::OnButton1使用::SendMessage的几个参数值和解释
hWnd = 0x002805d8 //HANDLE of CComboBox
nMsg = 0x0111 //WM_COMMAND
wParam = 0x0300 03e9 //EN_CHANGE + ID of CEdit
lParam = 0x026e0658 //HANDLE of CEdit

in USER32

AfxWndProcBase中所收到的参数值和解释
hWnd = 0x00df04ac //HANDLE of CDialog
nMsg = 0x0111 //WM_COMMAND
wParam = 0x0005 03f1 //CBN_EDITCHANGE + ID of CComboBox
lParam = 0x002805d8 //HANDLE of CComboBox

除了消息不变之外,WM_COMMAND,其他都不同了,不晓得怎么回事。因为这样的话就不能交给Combobox了,是交给CDialog的。
回复
cnzdgs 2008-03-26
是WM_COMMAND消息中的EN_CHANGE通知。
回复
cnzdgs 2008-03-26
你这种测试的想法是错的,ComboBox的内容改变是由它来通知其父窗口的,而不是由外部控件来通知它。
ComboBox内部包含了一个EDIT控件,EDIT控件的内容改变时会发消息给ComboBox,你在派生的ComboBox中响应EN_CHANGE消息试试。
回复
fangxu1999 2008-03-26
现在最想知道的就是:
//对话框中按钮发消息
void CMyDlg::OnButton1()
{
//是满足CWnd::SendDlgItemMessage的调用约定
SendDlgItemMessage(IDC_COMBO1, CBN_EDITCHANGE, IDC_COMBO1, (LPARAM)(GetDlgItem(IDC_COMBO1)->GetSafeHwnd()));
}

//对话框来处理消息
ON_CBN_EDITCHANGE(IDC_COMBO1, OnEditchangeCombo1)
void CMyDlg::OnEditchangeCombo1()
{
AfxMessageBox("CDlgTestDlg::OnEditchangeCombo1");
}

//组合框本身来处理消息
ON_CONTROL_REFLECT(CBN_EDITCHANGE, OnEditchange)
void CMyCombobox::OnEditchange()
{
AfxMessageBox("OnEditchange");
}

这个CBN_EDITCHANGE消息去哪里了?这两处可都没有收到
回复
fangxu1999 2008-03-26
[Quote=引用 11 楼 cnzdgs 的回复:]
你现在的按钮是做什么用的?是ComboBox和Button组成复合控件吗?
[/Quote]

现在在测试问题,搞懂之后就可以真正开始写代码了啊。
这是一个简单的基于对话框的程序。
一个普通的按钮,一个组合框,只不过这个组合框是和我自己派生出来的CMyCombobox的成员对象绑定在一起的。在CMyCombobox这个类中,提供了=CBN_EDITCHANGE处理方法。而在外面的对话框中也提供了CBN_EDITCHANGE的处理方法。处理方法都很easy,就是AfxMessageBox()。
那个按钮就是想发一个消息告诉组合框,你的内容改变了,请你自己更新一下列表内容——就是发出CBN_EDITCHANGE通知,不过我不想通知对话框,而是想告诉组合框自己。
(一般情况下,是组合框通知外面。而我想组合框通知它自己)
回复
cnzdgs 2008-03-26
你现在的按钮是做什么用的?是ComboBox和Button组成复合控件吗?
回复
fangxu1999 2008-03-26
[Quote=引用 9 楼 cnzdgs 的回复:]
在Windows中消息通常都是从子窗体发向其父窗体的,你应该在对话框中响应CBN_EDITCHANGE通知,然后调ComboBox类的函数或者再向ComboBox发消息。
[/Quote]

我也知道这样做确实很简单。但是我是希望做出一个相对独立的组合框。创建的时候通过某种方法喂一些数据,然后就可以让他自己处理自己的事了。不用外部干涉了啊
回复
cnzdgs 2008-03-26
在Windows中消息通常都是从子窗体发向其父窗体的,你应该在对话框中响应CBN_EDITCHANGE通知,然后调ComboBox类的函数或者再向ComboBox发消息。
回复
fangxu1999 2008-03-26
谢谢各位,不过如果使用CWnd::SendMessage(WM_COMMAND,...)将通知码放到WPARAM中,我感觉实际上是向父窗口CMyDialog发的通知消息,通知父窗口“组合框的内容发生了变化,请求处理”,然后试图让父窗口处理或转交给子窗口处理。
而测试的结果是父窗口——对话框直接处理了该消息,没有使用反射机制ON_CONTROL_REFLECT(CBN_EDITCHANGE, OnEditchange)交给子窗口——组合框来处理。看了代码执行过程发现也没有进入到ReflectLastMsg这个过程中去,也就是说没有机会给子窗口处理。

这样就不能达到让组合框自己本身来处理逻辑上应该属于自身的操作了

我认为CWnd::SendDlgItemMessage(IDC,...)是首先取出CWnd中制定IDC的控件的窗口句柄,然后直接向这个解析出来的窗口发消息,那么就会交给这个解析出来的子窗口来处理。不知道理解得对不对??

我所希望的是:要么向子窗口直接发命令(这样最好);要么通知父窗口但是通过反射之后还是交给子窗口处理。
如果向子窗口直接发命令的话,以上理解如果正确的话,大概必须CWnd::SendDlgItemMessage
而如果是通过反射机制,那么消息就先要流到父窗口处,然后ReflectLastMsg到子窗口,子窗口处理后就直接返回了,这样的话就应该使用CWnd::SendMessage(向this发消息)了。

对Windows消息很疑惑,想搞清楚。请大家帮忙解惑,谢谢。
回复
菜牛 2008-03-26
CComboBox本来有个FindSting方法好像就可以啊。
回复
yxq123 2008-03-26
MFC消息机制,系统会给每个应用程序创建一个消息队列,这个消息队列应用程序会循环调用。应用程序的大部分消息都进入这个队列。
回复
cnzdgs 2008-03-26
SendMessage(WM_COMMAND, MAKEWPARAM(IDC_COMBO1, CBN_EDITCHANGE), (LPARAM)(::GetDlgItem(m_hWnd, IDC_COMBO1)));

在MSDN中输入CBN_EDITCHANGE来查看相关说明。
回复
相关推荐
发帖
VC/MFC
创建于2007-09-28

1.5w+

社区成员

VC/MFC相关问题讨论
申请成为版主
帖子事件
创建了帖子
2008-03-26 12:05
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……