WIN 下的超动态菜单(二)用法
WIN 下的超动态菜单(二)用法
作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/
auto_dynamenu 是一个动态生成WINDOWS菜单的c++封装库,设计思路是要尽量简化动态菜单的生成代码,在程序界面任何地方想要显示菜单(特别是右键菜单)的时候,可以方便生成菜单,特别可以根据程序当时的内部数据,内部状态来生成不同的动态菜单。
auto_dynamenu 只封装了一个静态的接口函数,这样处理的目的是把类的实现代码可以放在头文件的类的声明里面,这样使用的时候只要包含头文件就可以直接调用了,不需要把实现文件加入工程,简化操作。
类的接口函数定义
/**************************************************************************************************\ * static int : 返回值,表明选择了哪个菜单项或者被更新的 nDefaultValue * dynamenu : * HWND hWnd : 当前窗口句柄 * LPPOINT pPoint : 显示菜单的位置,通常为0即可,自动确定显示的菜单位置 * char* pszMenu : 表明动态菜单内容的菜单字符串 * int nDefaultMode : 自动更新菜单选择标记的模式,0 无,1 等于模式,2 位模式 * int nDefaultValue : 缺省值,根据这个值,按照 nDefaultMode 来显示菜单项的选择标记 \**************************************************************************************************/ class auto_dynamenu
{
public:
static int dynamenu(HWND hWnd, LPPOINT pPoint, char* pszMenu, int nDefaultMode, int nDefaultValue);
};
参数:pszMenu
接口用一个格式化的字符串 pszMenu 来表示动态菜单,具体格式规则如下:
(1)每个菜单项为一个以 \n 结束的字符串
(2)菜单项的字符串通常以等号 = 分割为两部分,等号前面为要显示的菜单内容,等号后面为选择这个菜单后的取值
(3)菜单项的字符串里面如果没有等号,表明选择菜单返回的时候,返回的是菜单项字符串
(3)每个菜单项字符串前面可以加如下的修饰符:
[a] *(星号):表示这个菜单项前面有点的选择标记
[b] ^:表示这个菜单项前面有对勾的选择标记
[c] #:表示这个菜单项是灰色的
[d] -(减号):表明这个菜单项和下一个菜单项之间分为不同的列(多列的菜单)
[e] `(键盘左上角的按键):表示一个没有意义的占位符
[f] ~:表明是一个菜单分割线
(4)可以用单独一行的 ~ 表示一个菜单分割横线,“~\n”
(5)菜单项字符串等号前面部分的竖线字符”|“ 把菜单分割为父菜单和子菜单
(6)把不同的菜单项的字符串连接为一个完整的字符串就可以描述这个整个菜单了
例如:
char szMenu[] = "选项1=20\n" //选项1对应数值20,选择这个函数返回20 "选项2=0x20\n" //选择2对应数值0x20,选择这个函数返回0x20 "选项3=0\n" //选项3对应数值0,选择这个函数返回 INT_MAX(因为函数返回0代表没有选择菜单项,所以0用INT_MAX返回值表示) "~\n" //这个代表一个菜单分割横线 "录像|通用格式|avi格式=-1\n" //多级子菜单,选择这个返回-1 "录像|通用格式|~\n" //多级子菜单内部的分割横线 "录像|通用格式|mkv格式=-2\n" //多级子菜单,选择这个返回-2 "录像|~\n" "录像|专用格式|rdv格式=-3\n" //多级子菜单,选择这个返回-3 "~\n" "#暂停处理\n" //灰色禁用的菜单项,无法选择,可以用于显示信息 "~\n" "控制|^开始视频=10\n" //显示选中的视频开始菜单,选择返回10 "控制|结束视频=11\n" //显示没有选中的菜单,选择返回11 "播放当前录像 1.avi=d:\\1.avi\n" //返回文件名字符串指针 ;
上面的示例菜单字符串显示出来的菜单如下:
参数:hWnd
虽然菜单的消息不发往任何窗口,但是这个必须指定一个有效的窗口句柄,否则菜单显示不出来。通常指定对鼠标右键响应要显示菜单的窗口,或者主窗口。
参数:pPoint
菜单显示位置坐标POINT的指针,用于指定菜单的显示位置,是屏幕坐标。通常可以给0,这时程序自动选择菜单的显示位置,通常根据鼠标当前位置的控件类型确定,具体如下:
(1)如果是 BUTTON,工具栏上的按钮,则显示在这个控件的下方,左侧对齐
(2)如果是TreeView,ListView则显示在当前鼠标位置的条目的下方
(3)如果是TabCtrl则显示在鼠标当前的Tab页的页头的下方
(4)大小像个按钮的ActiveX控件,显示在下方
(5)其他显示在鼠标的当前位置
参数pPoint备注:
在这个参数给0的时候,代码自动确定菜单显示位置,但由于作者通常在VC6下编程(参见博文《我是如何把VC6一直用到2016年的》),因此代码中判断控件类型用的 ClassName 没有包含新版 Visual C 带的控件的类,可能需要使用者增加一些代码中的控件类名,对不同的控件确定不同的显示菜单的位置。具体情况参考文档《WIN 下超动态菜单(三)代码》。
参数:nDefaultMode
缺省模式,如果为0,没有缺省模式,菜单项的标记都在菜单字符串中指定。
如果为1,相等模式,如果某个菜单项的取值等于传入的nDefaultValue则显示选中标记。
如果为2,为与模式,如果某个菜单项的取值与nDefaultValue的位与不为0,则显示选中标记。
参数:nDefaultValue
当前的缺省值,配合nDefaultValue使用。
返回值:
程序应该根据返回值来判断选择了哪个菜单,根据nDefaultMode不同含义有差别,用法也有差异。
(1)如果nDefaultMode为0
(a)返回值为0的时候,表明没有选择任何菜单选项;
(b)返回值为INT_MAX,表明选择了某个值为0的菜单项;
(c)如果选择的菜单项字符串里面有等号,并且等号后面是数字(支持十进制和十六进制写法),则返回这个数字;
(d)如果选择的菜单项字符串里面的等号后面是字符串,则返回这个等号后面的字符串的指针;
(e)如果选择的菜单项字符串里面没有等号,则返回这个菜单项的字符串的指针。
(2)如果 nDefaultMode 为 1 或者 2
则返回nDefaultValue经过修改后的值,如果没有选择任何菜单,返回值等于nDefaultValue。
直接可以这样写 :
nDefaultValue = auto_dynamenu::dynamenu(GetSafeHwnd(), 0, pszMenu, nDefaultMode/*1 or 2*/, nDefaultValue);
动态菜单显示及根据返回值进行处理示例
char szMenu[] = "选项1=20\n" //选项1对应数值20,选择这个函数返回20 "选项2=0x20\n" //选择2对应数值0x20,选择这个函数返回20 "选项3=0\n" //选项3对应数值0,选择这个函数范围 INT_MAX(因为函数返回0代表没有选择菜单项,所以0用INT_MAX返回值表示) "~\n" //这个代表一个菜单分割横线 "录像|通用格式|avi格式=-1\n" //多级子菜单,选择这个返回-1,可以为负值 "录像|通用格式|~\n" //多级子菜单内部的分割横线 "录像|通用格式|mkv格式=-2\n" //多级子菜单,选择这个返回-2 "录像|~\n" "录像|专用格式|rdv格式=-3\n" //多级子菜单,选择这个返回-3 "~\n" "#暂停处理\n" //灰色禁用的菜单项,无法选择,可以用于显示信息 "~\n" "控制|^开始视频=10\n" //显示选中的视频开始菜单,选择返回10 "控制|结束视频=11\n" //显示没有选中的菜单,选择返回11 "播放当前录像 1.avi=d:\\1.avi\n" //返回文件名字符串指针 ; int index = auto_dynamenu::dynamenu(GetSafeHwnd(), 0, szMenu, 0, 0); switch (index) { case 0: //菜单没有选择,不做任何处理 break; case 20: //选项1 break; case 0x20: //选项2 break; case INT_MAX: //选项3,这个菜单项的值为0,通常可以避免这样的情况,就不需要处理这个特殊的值了 break; case -1: //avi 录像 break; case -2: //mkv录像 break; case -3: //rdv录像 break; case 10: //开始视频 break; case 11: //停止视频 break; default: { char * pfile = (char*)index; //选中了最后的文件菜单项,pfile为 “d:\\1.avi” 字符串的指针 } break; }
两点注释:
(1)上面的代码里面的菜单项的选中标记,都是在菜单字符串里面手工指定的,这时 nDefaultMode 和 nDefaultValue 指定为0。
(2)上面代码中的菜单项是在源代码中硬编码的,实际使用的时候可以动态生成,根据程序的状态来组建菜单字符串,例如:
char szMenu[1024] = {0}; int n = 0; if (value == 2) n += sprintf(szMenu + n, "^"); n += sprintf(szMenu + n, "值2=2\n"); if (value == 4) n += sprintf(szMenu + n, "^"); n += sprintf(szMenu + n, "值4=4\n");
nDefaultMode=1时的示例
(1)模式1为相等模式 ( nDefaultMode = 1 ),当某个菜单项的数值等于输入的 nDefaultValue 的时候,这个菜单项前面有选中标记
int val = 32; //nDefaultValue char szMenu[] = "整数1=1\n" "整数20=20\n" "整数32=32\n" "整数0x99=0x99\n" ; //注意上面的菜单字符串里面没有选中标记 //nDeaultMode = 1,当菜单项等于 nDefaultValue的时候显示选中标记 val = auto_dynamenu::dynamenu(GetSafeHwnd(), 0, szMenu, 1, val); //返回值为当前选中的菜单项对应的值,如果没有选择菜单,这个值保持原来的不变
nDefaultMode=2时的示例
模式2为位模式 ( nDefaultMode = 2 ),当菜单项的数值所对应的位 与nDefaultValue位与的时候不为0,则菜单项前面显示选中标记。
DWORD flags = 0x82; //当前的值 nDefaultValue char szMenu[] = "标记1=1\n" "标记2=2\n" "标记3=4\n" "标记4=8\n" "标记5=0x10\n" "标记6=0x20\n" "标记7=0x40\n" "标记8=0x80\n" ; //上面的菜单字符串内没有选择标记,程序自动根据 nDefaultValue 把对应的位加上选中标记 //nDefaultMode = 2 flags = (DWORD)auto_dynamenu::dynamenu(GetSafeHwnd(), 0, szMenu, 2, flags);
//返回值就是flags,如果没有选择菜单项,那么这个值不变
下载
可以在下面的链接下载代码和示例程序:
https://files.cnblogs.com/files/tomview/dynamenu_20160524.rar