WIN 下的超动态菜单(一)简介

WIN 下的超动态菜单(二)用法

WIN 下的超动态菜单(三)代码

作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/

超动态菜单的含义

         auto_dynamenu 是一个封装了 WINDOWS 菜单功能的 C++ 类库,用于动态生成 WINDOWS 菜单。所谓的动态有两个含义:

(1)菜单是动态创建和生成的;

(2)菜单本身的内容也是动态的,是可以根据程序的状态动态确定的

        因此这个封装类同把菜单预定义存储在 XML, INI, 资源内,运行时调用显示是有很大差别的,这个类方便动态显示同程序状态有关的,动态变化的菜单。

        譬如我的 高闯(高清智能交通违章检测抓拍系统)程序中,可以自动或手动录像,在录像没有开始的时候,菜单是如下的样子:

       

        当开始了录像之后,菜单不但可以显示开始录像的功能,还可以显示当前录像的文件名,长度,大小等信息,并增加停止录像的菜单,具体显示为如下样子:

       

        当同时启动了自动的监控录像的时候,菜单还可以显示如下:

       

        当录像结束之后,还可以增加一个显示播放上一个录像的菜单:

       

封装类的接口定义考虑

        封装类的接口定义是从易用,集中的角度考虑的:

(1)封装在类里面并在头文件中直接嵌入代码,是为了方便使用,包含头文件就可以了

(2)接口只设计了一个,避免 CreateMenu, InsertMenu, CheckMenu, 等有很多接口函数的封装方式,简化应用

(3)菜单定义和菜单处理的代码可以放在一起,而不是分散在代码中的各个不同地方,方便代码维护

代码的局限

(1)首要的局限就是这是一个 C++ 的 WINDOWS 代码封装类,现在谁还在 WINDOWS 下用 C++ 编程呢?

(2)自动确定菜单位置的代码部分根据控件类名确定菜单显示位置,新版的控件类名可能会有变化,需要更新代码

        当前代码中处理的控件类有:BUTTON,ThunderCommandButton,ThunderRT6CommandButton,AfxOleControl42sd,Afx:4000000:8,Afx:400000:8,AfxWnd42sd,ToolbarWindow32,TrayNotifyWnd,Shell_TrayWnd,NotifyIconOverflowWindow,SysTreeView32,SysListView32,SysTabControl32。

auto_dynamenu 代码

#ifndef __HSS_AUTO_DYNAMIC_MENU_HSS__
#define __HSS_AUTO_DYNAMIC_MENU_HSS__ /**************************************************************************************************\
动态创建菜单,并获取选择的菜单项加以处理 作者:黄山松,http://www.cnblogs.com/tomview/ 用法见示例程序,及作者在 博客园 的系列文章 "WIN 下的超动态菜单"
\**************************************************************************************************/ class auto_dynamenu
{
public: /**************************************************************************************************\
* static int : 返回值,表明选择了哪个菜单项
* dynamenu :
* HWND hWnd : 当前窗口句柄
* LPPOINT pPoint : 显示菜单的位置,通常为0即可,自动确定显示的菜单位置
* char* pszMenu : 表明动态菜单内容的菜单字符串
* int nDefaultMode : 自动更新菜单选择标记的模式,0 无,1 等于模式,2 位模式
* int nDefaultValue : 缺省值,根据这个值,按照 nDefaultMode 来显示菜单项的选择标记
\**************************************************************************************************/
static int dynamenu(HWND hWnd, LPPOINT pPoint, char* pszMenu, int nDefaultMode, int nDefaultValue)
{
int length = strlen(pszMenu);
for (int i = 0 ; i < length ; i ++)
{
if (pszMenu[i] == '\n')
pszMenu[i] = '\0';
} MENUITEMINFO mii;
memset(&mii, 0, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE|MIIM_ID|MIIM_STATE|MIIM_DATA;
mii.fType = MFT_STRING;
HMENU hMenu = ::CreatePopupMenu();
HMENU hParent = hMenu; char szSubMenu[256];
MENUITEMINFO miis;
memset(&miis, 0, sizeof(miis));
miis.cbSize = sizeof(miis);
miis.fMask = MIIM_SUBMENU | MIIM_TYPE;
miis.dwTypeData = szSubMenu; char* psz = pszMenu;
length = strlen(psz); int index = 0;
int cmd = 0;
BOOL bChecked = FALSE;
BOOL bGrayed = FALSE;
BOOL bSeperator = FALSE;
BOOL bRadio = FALSE;
BOOL bColumn = FALSE; while(length)
{
hParent = hMenu;
bChecked = FALSE;
bGrayed = FALSE;
bSeperator = FALSE;
bRadio = FALSE;
bColumn = FALSE; char* p; //2015年3月4日 增加,允许在前面有某些控制字符/////////////////////////////////////////////////////
char ctrl = 0;
if (psz[0] == '^' || psz[0] == '#' || psz[0] == '*')
{
ctrl = psz[0];
psz ++;
length --;
}
///////////////////////////////////////////////////////////////////////////////////////// while(p = strchr(psz, '|'))
{
*p = '\0'; miis.dwTypeData = szSubMenu;
miis.cch = sizeof(szSubMenu);
int sindex = 0;
for (BOOL fSu = GetMenuItemInfo(hParent, sindex, TRUE, &miis) ; fSu ; )
{
if (miis.dwTypeData && _stricmp(psz, miis.dwTypeData) == 0)
{
if (miis.hSubMenu)
hParent = miis.hSubMenu;
break;
}
sindex ++;
miis.dwTypeData = szSubMenu;
miis.cch = sizeof(szSubMenu);
fSu = GetMenuItemInfo(hParent, sindex, TRUE, &miis);
} if (!fSu)
{
//没找到,添加
HMENU hSubMenu = ::CreatePopupMenu();
::AppendMenu(hParent, MF_POPUP, (UINT)hSubMenu, psz);
hParent = hSubMenu;
} length -= (p - psz + 1);
psz = p + 1;
} while(length)
{
//2015年3月4日 新增加允许最前面有三种前置控制字符///////////////////////////////////////
if (ctrl == '^')
{
bChecked = TRUE;
ctrl = 0;
}
else if (ctrl == '*')
{
bRadio = TRUE;
bChecked = TRUE;
ctrl = 0;
}
else if (ctrl == '#')
{
bGrayed = TRUE;
ctrl = 0;
}
//////////////////////////////////////////////////////////////////////////////////// if (psz[0] == '-') //Break
{
//mii.fType = MFT_MENUBARBREAK | MFT_STRING;
bColumn = TRUE;
psz += 1;
length -= 1;
continue;
}
else if (psz[0] == '`') //2009年10月10日 忽略这个
{
psz += 1;
length -= 1;
continue;
}
else if (psz[0] == '~') //Seperator
{
bSeperator = TRUE;
psz += 1;
length -= 1;
if (length == 0)
{
//仅仅一个seperator
mii.fType = MFT_SEPARATOR;
::InsertMenuItem(hParent, index++, true, &mii);
psz += length + 1;
length = strlen(psz);
break;
}
continue;
}
else if (psz[0] == '^') //Check
{
bChecked = TRUE;
psz += 1;
length -= 1;
continue;
}
else if (psz[0] == '#') //Grayed
{
bGrayed = TRUE;
psz += 1;
length -= 1;
continue;
}
else if (psz[0] == '*')
{
bRadio = TRUE;
bChecked = TRUE;
psz += 1;
length -= 1;
continue;
} p = strchr(psz, '=');
if (p)
{
if (p[1] == '0' && (p[2] == 'X' || p[2] == 'x'))
{
sscanf(p+1, "%X", &cmd);
}
else if (p[1] == '\"') //2009年3月10日 强迫是字符串,不管是不是能够被转化为数字
{
cmd = (DWORD)(p+2);
}
else if (sscanf(p+1, "%d", &cmd) == 0)
{
cmd = (DWORD)(p+1);
}
*p = '\0';
}
else
{
cmd = (DWORD)psz;
} if (bSeperator)
{
mii.fType = MFT_SEPARATOR;
::InsertMenuItem(hParent, index++, true, &mii);
//mii.fType = MFT_STRING;
} mii.wID = index + 1;
mii.dwTypeData = psz;
mii.dwItemData = (DWORD)cmd;
if (bRadio)
mii.fType = MFT_STRING|MFT_RADIOCHECK;
else
mii.fType = MFT_STRING; mii.fState = bGrayed ? MFS_DISABLED : MFS_ENABLED; if (bChecked)
{
mii.fState |= MF_CHECKED;
}
else
{
switch(nDefaultMode)
{
case 0: //no
break;
case 2: //&
if (nDefaultValue & cmd)
mii.fState |= MF_CHECKED;
break;
case 1: //=
default:
if (nDefaultValue == cmd)
mii.fState |= MF_CHECKED;
break;
}
} if (bColumn)
{
mii.fType |= MFT_MENUBARBREAK;
} ::InsertMenuItem(hParent, index++, true, &mii); psz += length + 1; length = strlen(psz); break;
}
} POINT pt;
if (pPoint)
{
pt = *pPoint;
}
else
{
GetCursorPos(&pt);
HWND hChild = WindowFromPoint(pt);
if (hChild)// && hChild != hWnd) ???
{
char szClass[256];
int n = GetClassName(hChild, szClass, sizeof(szClass));
if (n)
{
RECT rect;
if (_stricmp(szClass, "BUTTON") == 0
|| _stricmp(szClass, "ThunderCommandButton") == 0
|| _stricmp(szClass, "ThunderRT6CommandButton") == 0
)
{
if (::GetWindowRect(hChild, &rect))
{
pt.x = rect.left;
pt.y = rect.bottom;
}
}
else if (_stricmp(szClass, "AfxOleControl42sd") == 0
|| _stricmp(szClass, "Afx:4000000:8") == 0
|| _stricmp(szClass, "Afx:400000:8") == 0
|| _stricmp(szClass, "AfxWnd42sd") == 0
)
{
//是个OCX控件
//看看大小,如果比较小,像个按钮,则在下面显示
if (GetWindowRect(hChild, &rect))
{
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
if (w > h && ((w < 200 && h < 100) || w > h * 2))
{
pt.x = rect.left;
pt.y = rect.bottom;
}
}
}
else if (_stricmp(szClass, "ToolbarWindow32") == 0)
{
//2014年6月5日 在win7中,可能在:溢出通知区域 ToolbarWindow32
//父窗口类: NotifyIconOverflowWindow
//再父窗口类:#32769 (Desktop) HWND hp = GetParent(GetParent(hChild)); if (hp) //win7的情况,在溢出通知栏,没有父窗口(spy++显示父窗口为桌面)
{
GetClassName(hp, szClass, sizeof(szClass)); if (_stricmp(szClass, "TrayNotifyWnd") == 0 //WinXp
|| _stricmp(szClass, "Shell_TrayWnd") == 0 //Win2000
)
{
//在通知区域显示的图片上面显示的
RECT rcp;
GetWindowRect(hp, &rcp);
pt.y = rcp.top - 1;
}
else
{
hp = 0;
}
} if (hp == 0)
{
//判断是不是在win7的溢出通知区域
hp = GetParent(hChild);
GetClassName(hp, szClass, sizeof(szClass));
if (_stricmp(szClass, "NotifyIconOverflowWindow") == 0)
{
//win7的溢出窗口类,直接在当前位置显示菜单即可
}
else
{
POINT ptc = pt;
::ScreenToClient(hChild, &ptc);
//但是在win7里面,为什么向图标的那个ToolbarWindow32发送tb_hittest会导致程序出错呢?
int index = SendMessage(hChild, TB_HITTEST, 0, (LPARAM)&ptc);
if (index >= 0)
{
if (SendMessage(hChild, TB_GETITEMRECT, index, (LPARAM)&rect))
{
pt.x = rect.left;
pt.y = rect.bottom + 1;
::ClientToScreen(hChild, &pt);
}
}
}
}
}
else if (_stricmp(szClass, "SysTreeView32") == 0)
{
POINT ptc = pt;
::ScreenToClient(hChild, &ptc);
TVHITTESTINFO ht;
ht.pt = ptc;
ht.hItem = 0;
ht.flags = 0;
HTREEITEM hItem = TreeView_HitTest(hChild, &ht);
if (hItem)
{
RECT rc;
if (TreeView_GetItemRect(hChild, hItem, &rc, TRUE))
{
//TreeView_GetItem
pt.x = rc.left;
//pt.x = ptc.x;
if (pt.x > 20)
pt.x -= 20;
pt.y = rc.bottom + 1;
::ClientToScreen(hChild, &pt);
}
}
}
else if (_stricmp(szClass, "SysListView32") == 0)
{
POINT ptc = pt;
::ScreenToClient(hChild, &ptc);
LVHITTESTINFO ht;
ht.pt = ptc;
ht.iItem = 0;
ht.iSubItem = 0;
ht.flags = 0; int iItem = ListView_SubItemHitTest(hChild, &ht);
if (iItem != -1)
{
RECT rc;
if (ListView_GetSubItemRect(hChild, ht.iItem, ht.iSubItem, LVIR_BOUNDS, &rc))
{
pt.x = rc.left;
pt.y = rc.bottom + 1;
::ClientToScreen(hChild, &pt);
}
}
}
else if (_stricmp(szClass, "SysTabControl32") == 0)
{
POINT ptc = pt;
::ScreenToClient(hChild, &ptc);
TCHITTESTINFO ti;
ti.flags = 0;
ti.pt = ptc; int iItem = TabCtrl_HitTest(hChild, &ti);
if (iItem != -1)
{
RECT rc;
if (TabCtrl_GetItemRect(hChild, iItem, &rc))
{
pt.x = rc.left;
pt.y = rc.bottom + 1;
::ClientToScreen(hChild, &pt);
}
}
}
}
}
} index = ::TrackPopupMenuEx(hMenu, TPM_RETURNCMD|TPM_NONOTIFY, pt.x, pt.y, hWnd, NULL); if (index)
{
mii.fMask = MIIM_DATA | MIIM_ID;
if (GetMenuItemInfo(hMenu, index, FALSE, &mii))
{
index = mii.dwItemData;
if (nDefaultMode == 2)
{
//index 可能返回0,表明所有的标记为都被取消选中了
if (nDefaultValue & index)
{
index = nDefaultValue & (~index);
//if (index == 0)
// index = INT_MAX;
}
else
{
index = nDefaultValue | index;
}
}
else if (nDefaultMode == 1)
{
if (index == 0)
index = nDefaultValue;
}
else if (index == 0) //2007-09-10 选择了0 的话,返回INT_MAX,而返回0表示取消或者出错
{
index = INT_MAX;
}
}
else
{
index = 0;
}
}
else if (nDefaultMode == 1)
{
index = nDefaultValue;
}
else if (nDefaultMode == 2)
{
index = nDefaultValue;
} ::DestroyMenu(hMenu); return index;
};
}; #endif

下载

可以在下面的链接下载代码和示例程序:

http://files.cnblogs.com/files/tomview/dynamenu_20160524.rar

WIN 下的超动态菜单(三)代码的更多相关文章

  1. WIN 下的超动态菜单(一)

    WIN 下的超动态菜单(一)介绍 WIN 下的超动态菜单(二)用法 WIN 下的超动态菜单(三)代码 作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/     ...

  2. WIN 下的超动态菜单(二)用法

    WIN 下的超动态菜单(一)简介 WIN 下的超动态菜单(二)用法 WIN 下的超动态菜单(三)代码 作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/     ...

  3. JS打字效果的动态菜单代码分享

    这篇文章主要介绍了JS打字效果的动态菜单,推荐给大家,有需要的小伙伴可以参考下. 这是一款基于javascript实现的打字效果的动态菜单特效代码,分享给大家学习学习. 小提示:浏览器中如果不能正常运 ...

  4. MFC之创建多级动态菜单

    一开始以我是这样做的,结果是错误的: 这段代码第一次点击时,会在第6个位置创建MFC菜单,我本以为再次点击,menu->GetSubMenu(5)返回的值就不会为空了,但事实是它返回了NULL, ...

  5. .net core3.1 abp动态菜单和动态权限(动态菜单实现和动态权限添加) (三)

    我们来创建动态菜单吧 首先,先对动态菜单的概念.操作.流程进行约束:1.Host和各个Tenant有自己的自定义菜单2.Host和各个Tenant的权限与自定义菜单相关联2.Tenant有一套默认的菜 ...

  6. SAAS云平台搭建札记: (三) AntDesign + .Net Core WebAPI权限控制、动态菜单的生成

    我们知道,当下最火的前端框架,非蚂蚁金服的AntDesign莫属,这个框架不仅在国内非常有名,在国外GitHub上React前端框架也排名第一.而且这个框架涵盖了React.Vue.Angular等多 ...

  7. 20款jquery下拉导航菜单特效代码分享

    20款jquery下拉导航菜单特效代码分享 jquery仿京东商城左侧分类导航下拉菜单代码 jQuery企业网站下拉导航菜单代码 jQuery css3黑色的多级导航菜单下拉列表代码 jquery响应 ...

  8. js下拉框二级关联菜单效果代码具体实现

    这篇文章介绍了js下拉框二级关联菜单效果代码具体实现,有需要的朋友可以参考一下 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transit ...

  9. Delphi编程中动态菜单要点归纳

      一.创建菜单并添加项目 在设计程序时,有时需要动态创建菜单, 通常使用以下的语句: PopupMenu1 := TPopupMenu.Create(Self);  Item := TMenuIte ...

随机推荐

  1. TCP的阻塞和重传机制

    TCP的阻塞和重传机制 网络拥堵 现在网络上大部分的网络请求都是以TCP的方式进行传输的了.网络链路是固定的,各种链路情况也是不一样的.网络拥堵一直是TCP协议设计和使用的时候尽力要避免的.比如,从T ...

  2. 分享一个UI与业务逻辑分层的框架(二)

    序言 第一篇讲解了UI与业务逻辑分层的框架(UIMediator)的使用.本篇将说明该框架的原理及代码实现. 整体结构 UI与后台类绑定主要分为UI输入->后台属性,后台属性-UI更新两部分,为 ...

  3. Linux基础知识之文件和目录的权限机制

    Linux中的用户 Linux中的用户有三类,分别是: 所有者(u) 同组用户(g) 其他人(o) 如下图所示,假设存在两个组:groupA和groupB,rachel和ross属于组groupA,m ...

  4. 怎样操作WebAPI接口(显示数据)

    就在去年Insus.NET已经写好的一个WebAPI项目,并且发布在IIS中.参考<创建与使用Web API>http://www.cnblogs.com/insus/p/5019088. ...

  5. JQuery的ajax

    JQuery-AJAX: jQuery load() 方法是简单但强大的 AJAX 方法. $(selector).load(URL,data,callback);(这三个参数可以随意设置几个) @ ...

  6. java timer 执行任务

    1. 建立timer import java.util.Timer; import java.util.TimerTask; public class Start { public class Sta ...

  7. 《TypeScript 中文入门教程》 1、基础数据类型

    转载:https://github.com/MyErpSoft/TypeScript-Handbook/blob/master/pages/zh-CHS/Basic%20Types.md 概述 为了让 ...

  8. MyEclipse10查看Struts2源码及Javadoc文档

    1:查看Struts2源码 (1):Referenced Libraries >struts2-core-2.1.6.jar>右击>properties. (2):Java Sour ...

  9. Android实现播放器功能

    package com.example.yzj.android_7_27; import android.content.ContentResolver;import android.content. ...

  10. powershell对txt文件的服务器进行ping操作

    powershell对txt文件的服务器进行ping操作,txt文件有几百台服务器要进行Ping操作.每行一个 #//***************************************** ...