vc中给树形控件的图标加上工具提示
我从没有在任何一个应用程序中看到过图标的工具提示。有时候查遍了整个帮助
文档也没有明白某个图标是什么意思。如果能在自己的程序中为图标加上工具提
示,一定会使界面的友好性大大增加。本文中以树形控件为例,详细介绍了在vc
中使用mfc提供的机制来实现图标工具提示的方法。
---- 第一步:使控件可以显示工具提示
---- 调用enabletooltips(true)使一个窗口可以显示工具提示。在什么地方插入
这条代码最好呢?在类的presubclasswindow()中。因为不管一个控件如何被创建
,mfc都会调用此函数。而其他的函数则不一定会被调用。以oncreate()为例,如
果调用create()或createex()创建一个控件,oncreate()会被调用,而如果一个
控件是从对话框资源创建,oncreate()就不会被调用。
实现代码如下:
void ctreectrlx::presubclasswindow()
{
ctreectrl::presubclasswindow();
enabletooltips(true);
}
---- 第二步:重载虚函数ontoolhittest()
---- mfc调用函数来确定在某个点是否应该显示工具提示。msdn建议如果鼠标落
在应该显示工具提示的点上,返回值1。这并不完全正确。这个函数应该返回不同
的值来区分窗口中不同的应该显示提示的区域。
---- 在这个函数中,本文只处理鼠标落在节点图标或节点状态图标上的情况。读
者可以按照自己的情况向树的其他元素上添加工具提示。在两种情况下,都要计
算图标的区域,并且把toolinfo的uid设为鼠标所在点的树节点的句柄。注意,尽
管对于节点图标和节点状态图标,使用了相同的id,但返回值并不相同。不同的
返回值迫使mfc更新工具提示。
---- 虽然我们可以在此函数中给出工具提示,但因为鼠标的每次移动都会调用此
函数,太多的处理并不是一个好注意,所以我们在其他的函数中处理应该显示什
么提示的问题。
类声明中的代码如下所示:
// overrides
// classwizard generated
virtual function overrides
//{{afx_virtual(ctreectrlx)
protected:
…
virtual int ontoolhittest
( cpoint point, toolinfo* pti ) const;
//}}afx_virtual
实现代码如下所示:
int ctreectrlx::ontoolhittest
(cpoint point, toolinfo * pti) const
{
rect rect;
uint nflags;
htreeitem hitem = hittest( point, &nflags );
if( nflags & tvht_onitemicon )
{
cimagelist *pimg = getimagelist( tvsil_normal );
imageinfo imageinfo;
pimg- >getimageinfo( 0, &imageinfo );
getitemrect( hitem, &rect, true );
rect.right = rect.left - 2;
rect.left -= (imageinfo.rcimage.right + 2);
pti- >hwnd = m_hwnd;
pti- >uid = (uint)hitem;
pti- >lpsztext = lpstr_textcallback;
pti- >rect = rect;
return pti- >uid;
}
else if( nflags & tvht_onitemstateicon )
{
cimagelist *pimg = getimagelist( tvsil_normal );
imageinfo imageinfo;
pimg- >getimageinfo( 0, &imageinfo );
getitemrect( hitem, &rect, true );
rect.right = rect.left -
(imageinfo.rcimage.right + 2);
pimg = getimagelist( tvsil_state );
rect.left = rect.right - imageinfo.rcimage.right ;
pti- >hwnd = m_hwnd;
pti- >uid = (uint)hitem;
pti- >lpsztext = lpstr_textcallback;
pti- >rect = rect;
// 返回与节点图标不同的值
return pti- >uid*2;
}
return -1;
}
---- 第三步:处理ttn_needtext消息;
---- 加入一个函数处理ttn_needtext消息通知。当工具处理控制需要知道应该显
示什么信息时,这条消息被发出。由于上一步中我们给toolinfo的lpsztext赋值
为lpstr_textcallback,所以我们要处理这个消息vc的classwizard并不支持这条
消息被映射,所以只有我们自己加入这条消息的映射机制加入到message_map中去
。我们不得不处理这个消息的两个版本,ttn_needtexta和ttn_needtexta。消息
映射的代码如下所示:
begin_message_map(ctreectrlx, ctreectrl)
//{{afx_msg_map(ctreectrlx)
…
//}}afx_msg_map
on_notify_ex_range
(ttn_needtextw, 0, 0xffff, ontooltiptext)
on_notify_ex_range
(ttn_needtexta, 0, 0xffff, ontooltiptext)
end_message_map()
下面的代码是加到类声明中:
protected:
//{{afx_msg(ctreectrlx)
…
//}}afx_msg
afx_msg bool ontooltiptext
( uint id, nmhdr * pnmhdr, lresult * presult );
declare_message_map()
---- 现在讨论这个函数本身的实现。为了适应不同的语言字符集,ansi字符集和
unicode字符集都必须被处理,处理过程会有些不同。此处对树形控件的本身产生
的tooltip消息不予处理,过滤掉上述消息的原则是树形控件本身产生的消息的id
是树形控件窗口的句柄,并且有ttf_idishwnd标志。根据鼠标位置可以确定应该
给出节点图标还是状态图标的工具提示。本文根据笔者画的图显示了一些无关紧
要的提示,读者做这一步时应该加入一些有意义的提示。当然,本文假定控件包
含节点图标和状态图标。如不包含,计算鼠标位置时要注意 不要计算错误。
bool ctreectrlx::ontooltiptext
( uint id, nmhdr * pnmhdr, lresult * presult )
{
// 需要处理ansi和unicode两种格式
tooltiptexta* pttta = (tooltiptexta*)pnmhdr;
tooltiptextw* ptttw = (tooltiptextw*)pnmhdr;
cstring strtiptext;
uint nid = pnmhdr- >idfrom;
// 不必处理树自己发出的tooltip消息
if( nid == (uint)m_hwnd &&
(( pnmhdr- >code == ttn_needtexta &&
pttta- >uflags & ttf_idishwnd ) ||
( pnmhdr- >code == ttn_needtextw &&
ptttw- >uflags & ttf_idishwnd ) ) )
return false;
// 得到鼠标位置
const msg* pmessage;
cpoint pt;
pmessage = getcurrentmessage();
assert ( pmessage );
pt = pmessage- >pt;
screentoclient( &pt );
uint nflags;
htreeitem hitem = hittest( pt, &nflags );
if( nflags & tvht_onitemicon )
{
int nimage, nselimage;
getitemimage( (htreeitem ) nid, nimage, nselimage );
switch(nimage)
{
case 0:
strtiptext = "叉";
break;
case 1:
strtiptext = "加号";
break;
case 2:
strtiptext = "菱形";
break;
}
}
else
{
if( (getitemstate( (htreeitem ) nid,
tvis_stateimagemask ) > >12 ) == 2 )
strtiptext.format( "此节点被选中" );
else
strtiptext.format( "此节点未被选中" );
}
#ifndef _unicode
if (pnmhdr- >code == ttn_needtexta)
lstrcpyn(pttta- >sztext, strtiptext, 80);
else
_mbstowcsz(ptttw- >sztext, strtiptext, 80);
#else