我在基于对话框的程序中使用了tree ctrl,我想单击并释放父项目时即展开子项目(MSN好像也是这怎么做的),我映射了消息如下:
void CMyTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{....
if(...)
{ 展开子项目}}
但是只有双击时,或者单击tree ctrl的空白处(即不是项目处)释放时
才进入该函数,这是为什么啊?
void CMyTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{....
if(...)
{ 展开子项目}}
但是只有双击时,或者单击tree ctrl的空白处(即不是项目处)释放时
才进入该函数,这是为什么啊?
要实现这个功能,现在有两个问题要解决,一个是如何捕捉到WM_LBUTTONUP消息,二是当鼠标点ITEM的BUTTON时,该ITEM会立刻展开或者关闭,不会等到鼠标放开,因此要屏蔽掉这个缺省的展开动作,改成当鼠标放开时才展开或者关闭。经过跟踪鼠标消息,发现当鼠标点在ITEM的LABEL上,不拖动鼠标,然后放开,系统发送的消息是WM_LBUTTONDOWN和WM_MOUSEMOVE(注意,是WM_MOUSEMOVE,不是WM_LBUTTONUP,不知道是什么原因)。如果鼠标点在ITEM的LABEL上,然后拖动鼠标,最后释放鼠标,发送的是WM_LBUTTONDOWN, WM_MOUSEMOVE和WM_LBUTTONUP消息。因此我们需要处理这三个消息。从CTreeCtrl派生一个类,称为CMyTreeCtrl, 定义一个BOOL型的成员变量m_bLButtonDown和HTREEITEM型的变量m_hItemHitted用来记录鼠标左健是否已经被按下以及被点中的ITEM,并在构造函数中初始化为。构造函数和三个消息响应函数分别为
CMyTreeCtrl::CMyTreeCtrl()
{
m_bLButtonDown = FALSE ;
m_hItemHitted = NULL ;
}
void CMyTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
UINT flag ;
HTREEITEM hItemHitted = HitTest( point, &flag ) ;
if( flag & TVHT_ONITEMLABEL )
{
m_bLButtonDown = TRUE ;
m_hItemHitted = hItemHitted ;
}
CTreeCtrl::OnLButtonDown(nFlags, point);
}
void CMyTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
m_bLButtonDown = FALSE ;
UINT flag ;
HTREEITEM hItemHitted = HitTest( point, &flag ) ;
if( ( hItemHitted != NULL && flag&TVHT_ONITEMBUTTON ) ||
( hItemHitted == m_hItemHitted && flag&TVHT_ONITEMLABEL ) )
Expand( hItemHitted, TVE_TOGGLE ) ;
CTreeCtrl::OnLButtonUp(nFlags, point);
}void CMyTreeCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
UINT flag ;
HTREEITEM hItemHitted = HitTest( point, &flag ) ;
if( hItemHitted !=NULL && ( flag & TVHT_ONITEMLABEL ) )
{
if( m_bLButtonDown == TRUE && (!(nFlags & MK_LBUTTON )) )
{
Expand( hItemHitted, TVE_TOGGLE ) ;
m_bLButtonDown = FALSE ;
}
}
CTreeCtrl::OnMouseMove(nFlags, point);
}
最后屏蔽掉鼠标点击ITEM的BUTTON时的缺省动作,这时要处理NM_CLICK消息,如下
void CMyTreeCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
CPoint pt ;
UINT flag ;
GetCursorPos( &pt ) ;
ScreenToClient( &pt ) ;
HTREEITEM hItemHitted = HitTest( pt, &flag ) ;
if( hItemHitted != NULL && ( flag&TVHT_ONITEMBUTTON ) )
*pResult = 1 ; // 返回值为1,父窗口不处理这个消息
else
*pResult = 0;
}消息映射为
BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(CMyTreeCtrl)
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
我的方法如下:
首先我把TreeCtrl的HAS_BUTTON 给关了(干净彻底),然后我把判断项目以及展开的代码都放进了
CMyTreeCtrl::OnClick中,最后,我在CMyTreeCtrl::OnDBClick 中把 *pResult = 0;改成了 *pResult = 1 ,也实现了上述功能!
疑惑的是:
1、就是WM_LBUTTONDOWN好像是标准的WINDOWS消息吧,到了TreeCtrl中怎么就被封装进了NM_CLICK中了,像这样的消息“NM_CLICK”用spy++好象没有看到!
2、只要我实现了CMyTreeCtrl::OnDBClick的消息映射,即使一个字也不改,那么我的父对话框的关于TreeCtrl资源的OnDBClick事件的消息映射函数如:CXXXDlg::OnDblclkTreectrl()
就无法进入了,请问类似这种控件资源的消息路由究竟在MFC里是怎样的?它发给控件类本身的消息映射后就不再发给父窗体的消息映射了?dingsun2(白玉京) 你说的那个TVN_SELCHANGED事件 没有办法实现这样的情况(点击某一item展开,再点击该item要求收缩但TVN_SELCHANGED事件却未被触发!)