Toolbar制作菜单条过程详解
详解 过程 制作 菜单 ToolBar
2023-06-13 09:13:51 时间
文章来源:互联网作者:ggg82/CSDN
现在许多用户界面都使用工具栏制作菜单条,小弟最近对此感兴趣,便从网上求助,可是得到的帮助大多是BCGControlBar的源代码或者是SizableRebar的源代码,对于只希望是自己的界面具有该功能的朋友来说,这也许是不错的选择,只要看一下demo,然后直接调用别人的类库就可以了,但对于我等对此话题感兴趣,希望弄懂其来龙去脉的读者来说,直接看这些没有详细解释的源代码,要从中弄出个所以然来,实不是件容易的是,至少对于像我这样的菜鸟来说是这样的,本文出于此种原因,希望对还在寻求此帮助的读者能提供一些帮助。
下面我们边看边侃:
在接收到toolbarbutton按下消息时,我们一般使用TrackPopupMenuEx弹出菜单,问题的关键是,在菜单未关闭时,TrackPopupMenuEx并不返回,并拦截鼠标和键盘消息,使用spy可以看到,此时的工具栏收不到任何消息,当然无从改变热点,这就需要我们自己探测鼠标位置并在鼠标移动到下一个热点时关闭上一个菜单并显示下一个菜单。这里我们使用钩子函数SetWindowsHookEx在调用TrackPupupMenuEx前安装WH_MSGFILTER钩子,代码如下:
m_hMsgHook=SetWindowsHookEx(WH_MSGFILTER,MessageProc,0,GetCurrentThreadId());
MssageProc是钩子函数,代码如下:
LRESULTCALLBACK MessageProc(intcode,WPARAMwParam,LPARAMlParam)
{
if(code==MSGF_MENU)
{
HookMessageProc(lParam);
}
returnCallNextHookEx(m_hMsgHook,code,wParam,lParam);
}
函数检查消息,如果是来自菜单,则将消息传递给函数HookMessageProc处理,我们所要做的就是在该函数中检测消息WM_MOUSEMOVE,并测试鼠标位置,如果鼠标已经移动到另一个按钮上,则关闭菜单并显示下一个菜单,关闭菜单使用消息WM_CANCELMODE,当菜单关闭后,我们要释放钩子,在下一个菜单弹出时重新安装钩子,弹出菜单示例代码如下:
voidHookMessageProc(MSG*pMsg)
{
if(pMsg->message==WM_MOUSEMOVE)
{
intiButton,iCount;
POINTpt={LOWORD(pMsg->lParam),HIWORD(pMsg->lParam)};
ScreenToClient(hWndToolbar,&pt);
iButton=SendMessage(hWndToolbar,TB_HITTEST,0,&pt);
iCount=SendMessage(hWndToolbar,TB_BUTTONCOUNT,0,0);
if(iPopup!=iButton&&iButton=0)
{
iNextPop=iButton;
SendMessage(hWndMain,WM_CANCELMODE,0,0);
}
else
{
iNextPop=-1;
}
}
}
(经验与建议:不要试图在此处调用TrackPopup,我曾试图取消该函数内的while循环,直接在此调用该函数,结果是在TrackPopupMenuEx未返回之前,该函数已被调用)
这里,仅仅处理了鼠标移动消息,真正的菜单还应处理键盘导航消息,详细的代码可以参考
BCGControlBar(http://www.vckbase.com/code/downcode.asp?id=1382)
或SizableRebar(http://www.codeproject.com/docking/sizablerebar/SizableRebar_demo.zip
)
有了这底层框架,这些处理过程应该不再困难,文章所涉及到的一些API函数可以参考msdn。
Msdn上相关资料:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/faq/iemenubar.asp
http://www.microsoft.com/msj/0199/c/c0199.aspx
下面我们边看边侃:
在接收到toolbarbutton按下消息时,我们一般使用TrackPopupMenuEx弹出菜单,问题的关键是,在菜单未关闭时,TrackPopupMenuEx并不返回,并拦截鼠标和键盘消息,使用spy可以看到,此时的工具栏收不到任何消息,当然无从改变热点,这就需要我们自己探测鼠标位置并在鼠标移动到下一个热点时关闭上一个菜单并显示下一个菜单。这里我们使用钩子函数SetWindowsHookEx在调用TrackPupupMenuEx前安装WH_MSGFILTER钩子,代码如下:
m_hMsgHook=SetWindowsHookEx(WH_MSGFILTER,MessageProc,0,GetCurrentThreadId());
MssageProc是钩子函数,代码如下:
LRESULTCALLBACK MessageProc(intcode,WPARAMwParam,LPARAMlParam)
{
if(code==MSGF_MENU)
{
HookMessageProc(lParam);
}
returnCallNextHookEx(m_hMsgHook,code,wParam,lParam);
}
函数检查消息,如果是来自菜单,则将消息传递给函数HookMessageProc处理,我们所要做的就是在该函数中检测消息WM_MOUSEMOVE,并测试鼠标位置,如果鼠标已经移动到另一个按钮上,则关闭菜单并显示下一个菜单,关闭菜单使用消息WM_CANCELMODE,当菜单关闭后,我们要释放钩子,在下一个菜单弹出时重新安装钩子,弹出菜单示例代码如下:
voidTrackPopup(HWNDhWndToolBar,intiButton)
{
while(iButton>=0)
{
SendMessage(hWndToolBar,TB_SETHOTITEM,iButton,0);
iPopup=iButton;
//安装钩子
g_hMsgHook=SetWindowsHookEx(WH_MSGFILTER,MessageProc,0,GetCurrentThreadId());
//弹出菜单
TrackPopupMenuEx(…);
//卸载钩子
UnhookWindowsHookEx(g_hMsgHook);
iButton=iNextPop;//下一个弹出项,若为负,则退出
}
SendMessage(hWndToolBar,TB_SETHOTITEM,-1,0);
}
(经验与建议:如果button使用样式TBSTYLE_DROPDOWN,请不要在消息TBN_DROPDOWN中直接调用该函数,应使用中间消息,然后使用PostMessa个发送该消息,以使TBN_DROPDOWN可以直接返回,否则消除第一个高亮热点是很麻烦的事。)
iPopup为当前弹出项,iNextPop为下一个弹出项,这些变量需要在函数HookMessageProc中处理,示例代码如下:
voidHookMessageProc(MSG*pMsg)
{
if(pMsg->message==WM_MOUSEMOVE)
{
intiButton,iCount;
POINTpt={LOWORD(pMsg->lParam),HIWORD(pMsg->lParam)};
ScreenToClient(hWndToolbar,&pt);
iButton=SendMessage(hWndToolbar,TB_HITTEST,0,&pt);
iCount=SendMessage(hWndToolbar,TB_BUTTONCOUNT,0,0);
if(iPopup!=iButton&&iButton=0)
{
iNextPop=iButton;
SendMessage(hWndMain,WM_CANCELMODE,0,0);
}
else
{
iNextPop=-1;
}
}
}
(经验与建议:不要试图在此处调用TrackPopup,我曾试图取消该函数内的while循环,直接在此调用该函数,结果是在TrackPopupMenuEx未返回之前,该函数已被调用)
这里,仅仅处理了鼠标移动消息,真正的菜单还应处理键盘导航消息,详细的代码可以参考
BCGControlBar(http://www.vckbase.com/code/downcode.asp?id=1382)
或SizableRebar(http://www.codeproject.com/docking/sizablerebar/SizableRebar_demo.zip
)
有了这底层框架,这些处理过程应该不再困难,文章所涉及到的一些API函数可以参考msdn。
Msdn上相关资料:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/faq/iemenubar.asp
http://www.microsoft.com/msj/0199/c/c0199.aspx
相关文章
- Dell R940xa服务器通过iDRAC管理口安装Centos7.2过程详解
- c++中vector的用法详解_vector>初始化
- 详解Lombok 的使用,工作原理,优缺点
- 使用Redis有序集合实现IP归属地查询详解
- PostgreSQL字符类型长度变更的性能详解数据库
- mysql之存储过程学习详解数据库
- Sybase 存储过程中IF的用法详解数据库
- mysql时间与字符串之间相互转换详解数据库
- Lucene学习总结之七:Lucene搜索过程解析(3)详解架构师
- MapReduce 过程详解大数据
- Java数据结构学习笔记之三Java数据结构与算法之队列(Queue)实现详解编程语言
- Java 在使用迭代器迭代集合的过程中的注意事项详解编程语言
- oracle操作字符串:拼接、替换、截取、查找、长度、判断详解编程语言
- Servlet主要相关类核心类 容器调用的过程浅析 servlet解读 怎么调用 Servlet是什么 工作机制详解编程语言
- java时间工具类详解编程语言
- PHP获取代码段执行的毫秒时间和消耗内存详解编程语言
- PHP扩展插件imagick使用笔记详解编程语言
- windows 下为Python安装redis详解编程语言
- Linux fdisk命令创建逻辑分区过程详解
- 详解MySQL下载,一步步教您安装(mysql下载过程)
- 深入mysql创建自定义函数与存储过程的详解
- PHP迭代器的内部执行过程详解
- oracle索引介绍(图文详解)
- SqlServer使用cursor处理重复数据过程详解