当前位置: 首页 > news >正文

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...

(本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)

接上一节: (C#)Windows Shell 外壳编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单

上一节说到如何弹出 IShellFolder 的上下文菜单,也就是 IContextMenu。有时候我们需要在这个菜单上面,加入一些属于自己的菜单项。举个例子,你打开资源管理器,查看左边目录树的右键菜单,会发现顶层多了一个折叠/展开的菜单项。好,我们也动手来加入这个菜单项。

修改例子3,我们找到 QueryContextMenu 处,这时候提供了一个菜单句柄:

None.gif // 提供一个弹出式菜单的句柄
None.gif
IntPtr contextMenu  =  API.CreatePopupMenu();
None.gifiContextMenu.QueryContextMenu(contextMenu, 
0 ,
None.gif    API.CMD_FIRST, API.CMD_LAST, CMF.NORMAL 
|  CMF.EXPLORE);

然后增加以下代码:

ExpandedBlockStart.gif ContractedBlock.gif /**/ /增加一个自定义菜单
None.gif string  topInvoke  =  Tree1.SelectedNode.IsExpanded  ?   " 折叠(&A) "  :  " 展开(&A) " ;
None.gifMFT extraFlag 
=  (Tree1.SelectedNode.Nodes.Count  >   0 ?   0  : MFT.GRAYED;
None.gifAPI.InsertMenu(contextMenu, 
0 , MFT.BYPOSITION  |  extraFlag,
None.gif    (
int )(API.CMD_LAST + 1 ), topInvoke);
None.gif
// 增加分隔线
None.gif
API.InsertMenu(contextMenu,  1 , MFT.BYPOSITION  |  MFT.SEPARATOR,  0 " - " );
None.gif
// 把第一项菜单设置为默认菜单,也就是加粗
None.gif
API.SetMenuDefaultItem(contextMenu,  0 true );
ExpandedBlockStart.gifContractedBlock.gif
/**/ /

这里我们用到了 InsertMenu 这个 API:

None.gif [DllImport( " user32 " ,
None.gif            SetLastError 
=   true ,
None.gif            CharSet 
=  CharSet.Auto)]
None.gif        
public   static   extern   bool  InsertMenu(
None.gif            IntPtr hmenu,
None.gif            
uint  uPosition,
None.gif            MFT uflags,
None.gif            
uint  uIDNewItem,
None.gif            [MarshalAs(UnmanagedType.LPTStr)]
None.gif            
string  lpNewItem);

参数2表示增加菜单项的位置,从0开始。
参数3表示flag,这里提供了菜单状态,以及位置的计算方法,它是一个枚举:

ContractedBlock.gif ExpandedBlockStart.gif public enum MFT
None.gifpublic enum MFT : uint
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    GRAYED 
= 0x00000003,
InBlock.gif    DISABLED 
= 0x00000003,
InBlock.gif    CHECKED 
= 0x00000008,
InBlock.gif    SEPARATOR 
= 0x00000800,
InBlock.gif    RADIOCHECK 
= 0x00000200,
InBlock.gif    BITMAP 
= 0x00000004,
InBlock.gif    OWNERDRAW 
= 0x00000100,
InBlock.gif    MENUBARBREAK 
= 0x00000020,
InBlock.gif    MENUBREAK 
= 0x00000040,
InBlock.gif    RIGHTORDER 
= 0x00002000,
InBlock.gif    BYCOMMAND 
= 0x00000000,
InBlock.gif    BYPOSITION 
= 0x00000400,
InBlock.gif    POPUP 
= 0x00000010
ExpandedBlockEnd.gif}

MF_BYPOSITION 表示位置的计算方法是使用索引项,第一个菜单就是0,第二个菜单就是1,如此类推...

参数4表示命令值。我们可以根据这个命令值来执行对应的功能。

然后就可以弹出菜单了:

None.gif // 弹出菜单
None.gif
uint  cmd  =  API.TrackPopupMenuEx(contextMenu,TPM.RETURNCMD,
None.gifMousePosition.X, MousePosition.Y, 
this .Handle, IntPtr.Zero);

可以看到弹出菜单的效果。当然,我们还必须做点事情来响应这个菜单的执行:

None.gif // 获取命令序号,执行菜单命令
None.gif
if  (cmd  >=  API.CMD_FIRST)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    dot.gifdot.gif
InBlock.gif    
//自定义菜单命令
InBlock.gif
    if (cmd == API.CMD_LAST + 1)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
if (Tree1.SelectedNode.IsExpanded)
InBlock.gif            Tree1.SelectedNode.Collapse();
InBlock.gif        
else
InBlock.gif            Tree1.SelectedNode.Expand();
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}


如图:



执行菜单命令

能不能不弹出菜单直接调用菜单项相应的命令?答案是肯定的。

大家还记得怎么显示一个文件或文件夹的属性对话框吗?

Yes,用ShellExecuteEx并指定SHELLEXECUTEINFO的lpVerb域为properties就可,但是这种方法只能查看一个文件的属性,怎么同时查看多个的?

要知道ShellExecuteEx查看文件属性最终也是靠IContextMenu帮忙的,所以答案还是在IContextMenu上,我们只要在调用GetUIObjectOf时把想查看的文件或文件件的PIDL做为参数传进去,然后直接调用InvokeCommand方法就OK啦。

当然,我们做的例子,还是弹出一个对象的属性,靠你自己修改了。

我们必须先得到 IContextMenu 接口:

None.gif // 得到 IContextMenu 接口
None.gif
IntPtr iContextMenuPtr  =  IntPtr.Zero;
None.gifiContextMenuPtr 
=  IParent.GetUIObjectOf(IntPtr.Zero, ( uint )pidls.Length,
None.gif    pidls, 
ref  Guids.IID_IContextMenu,  out  iContextMenuPtr);
None.gifIContextMenu iContextMenu 
=  (IContextMenu)Marshal.GetObjectForIUnknown(iContextMenuPtr);

但我们不弹出这个菜单,仅仅是调用 InvokeCommand 来执行命令而已:

None.gif // 直接执行命令
None.gif
CMINVOKECOMMANDINFOEX invoke  =   new  CMINVOKECOMMANDINFOEX();
None.gifinvoke.cbSize 
=  Marshal.SizeOf( typeof (CMINVOKECOMMANDINFOEX));
None.gifinvoke.lpVerb 
=  Marshal.StringToHGlobalAnsi( " properties " );
None.gifinvoke.lpDirectory 
=   string .Empty;
None.gifinvoke.fMask 
=   0 ;
None.gifinvoke.nShow 
=   1 ;
None.gifiContextMenu.InvokeCommand(
ref  invoke);

关于verb的更多信息请参考MSDN。我这里做的是执行“属性”,如果你要执行其他命令,或者按照索引来执行,也是可以的。这里不做深入研究。



源代码:/Files/lemony/WinShell4.rar

我正在考虑下一节是讲图标,还是继续讲iContextMenu。大家也知道,某个文件的右键菜单里面,往往会有几个 winrar 的选项,还带着可爱的图标。



很有可能下一节就讲述如何在C#中也实现这样的效果哦。希望大家多多支持^_^。

相关文章:

  • phalcon模型查询几种方法
  • 在DotNetNuke中通过修改ascx文件源码自定义界面
  • day10---paramiko ssh ftp
  • ArcSDE9.2
  • 如何删除mspcidrv.sys病毒
  • js循环生成多个easyui datagrid数据网格时,初始化表格
  • 双绞线 (Twist-Pair)
  • jquery cookie插件轻松实现切换背景颜色
  • 我儿小乐乐
  • asp.net中使用modal window的问题
  • Python--命令行参数解析Demo
  • 股票基本名词概念
  • CSS3动画特效——transform详解
  • 词典建立过程缓慢的解决~~子系统构架重新设计!
  • Angular通过CORS实现跨域方案
  • JavaScript-如何实现克隆(clone)函数
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 30秒的PHP代码片段(1)数组 - Array
  • C# 免费离线人脸识别 2.0 Demo
  • Date型的使用
  • laravel with 查询列表限制条数
  • node入门
  • Puppeteer:浏览器控制器
  • uni-app项目数字滚动
  • Webpack 4 学习01(基础配置)
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 分享自己折腾多时的一套 vue 组件 --we-vue
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 双管齐下,VMware的容器新战略
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 通信类
  • 为视图添加丝滑的水波纹
  • 我这样减少了26.5M Java内存!
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • 责任链模式的两种实现
  • nb
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • #在 README.md 中生成项目目录结构
  • (42)STM32——LCD显示屏实验笔记
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算
  • ***详解账号泄露:全球约1亿用户已泄露
  • .chm格式文件如何阅读
  • .net core Swagger 过滤部分Api
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • @取消转义
  • [ASP.NET 控件实作 Day7] 设定工具箱的控件图标
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [c]扫雷
  • [C++][基础]1_变量、常量和基本类型
  • [C++]C++基础知识概述
  • [CSAWQual 2019]Web_Unagi ---不会编程的崽
  • [DP 训练] Longest Run on a Snowboard, UVa 10285