(本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)
接上一节:
(C#)Windows Shell 外壳编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单
上一节说到如何弹出 IShellFolder 的上下文菜单,也就是 IContextMenu。有时候我们需要在这个菜单上面,加入一些属于自己的菜单项。举个例子,你打开资源管理器,查看左边目录树的右键菜单,会发现顶层多了一个折叠/展开的菜单项。好,我们也动手来加入这个菜单项。
修改例子3,我们找到 QueryContextMenu 处,这时候提供了一个菜单句柄:
//
提供一个弹出式菜单的句柄
IntPtr contextMenu
=
API.CreatePopupMenu();
iContextMenu.QueryContextMenu(contextMenu,
0
,
API.CMD_FIRST, API.CMD_LAST, CMF.NORMAL
|
CMF.EXPLORE);
然后增加以下代码:
/**/
/增加一个自定义菜单
string
topInvoke
=
Tree1.SelectedNode.IsExpanded
?
"
折叠(&A)
"
:
"
展开(&A)
"
;
MFT extraFlag
=
(Tree1.SelectedNode.Nodes.Count
>
0
)
?
0
: MFT.GRAYED;
API.InsertMenu(contextMenu,
0
, MFT.BYPOSITION
|
extraFlag,
(
int
)(API.CMD_LAST
+
1
), topInvoke);
//
增加分隔线
API.InsertMenu(contextMenu,
1
, MFT.BYPOSITION
|
MFT.SEPARATOR,
0
,
"
-
"
);
//
把第一项菜单设置为默认菜单,也就是加粗
API.SetMenuDefaultItem(contextMenu,
0
,
true
);
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**/
/
这里我们用到了 InsertMenu 这个 API:
[DllImport(
"
user32
"
,
SetLastError
=
true
,
CharSet
=
CharSet.Auto)]
public
static
extern
bool
InsertMenu(
IntPtr hmenu,
uint
uPosition,
MFT uflags,
uint
uIDNewItem,
[MarshalAs(UnmanagedType.LPTStr)]
string
lpNewItem);
参数2表示增加菜单项的位置,从0开始。
参数3表示flag,这里提供了菜单状态,以及位置的计算方法,它是一个枚举:
public enum MFT
public enum MFT : uint
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
GRAYED = 0x00000003,
DISABLED = 0x00000003,
CHECKED = 0x00000008,
SEPARATOR = 0x00000800,
RADIOCHECK = 0x00000200,
BITMAP = 0x00000004,
OWNERDRAW = 0x00000100,
MENUBARBREAK = 0x00000020,
MENUBREAK = 0x00000040,
RIGHTORDER = 0x00002000,
BYCOMMAND = 0x00000000,
BYPOSITION = 0x00000400,
POPUP = 0x00000010
}
MF_BYPOSITION 表示位置的计算方法是使用索引项,第一个菜单就是0,第二个菜单就是1,如此类推...
参数4表示命令值。我们可以根据这个命令值来执行对应的功能。
然后就可以弹出菜单了:
//
弹出菜单
uint
cmd
=
API.TrackPopupMenuEx(contextMenu,TPM.RETURNCMD,
MousePosition.X, MousePosition.Y,
this
.Handle, IntPtr.Zero);
可以看到弹出菜单的效果。当然,我们还必须做点事情来响应这个菜单的执行:
//
获取命令序号,执行菜单命令
if
(cmd
>=
API.CMD_FIRST)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![dot.gif](https://www.cnblogs.com/Images/dot.gif)
![dot.gif](https://www.cnblogs.com/Images/dot.gif)
//自定义菜单命令
if (cmd == API.CMD_LAST + 1)
![ExpandedSubBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (Tree1.SelectedNode.IsExpanded)
Tree1.SelectedNode.Collapse();
else
Tree1.SelectedNode.Expand();
}
}
如图:
![](https://i-blog.csdnimg.cn/blog_migrate/a8f596fb359a19683ee47e4b553077c0.jpeg)
执行菜单命令
能不能不弹出菜单直接调用菜单项相应的命令?答案是肯定的。
大家还记得怎么显示一个文件或文件夹的属性对话框吗?
Yes,用ShellExecuteEx并指定SHELLEXECUTEINFO的lpVerb域为properties就可,但是这种方法只能查看一个文件的属性,怎么同时查看多个的?
要知道ShellExecuteEx查看文件属性最终也是靠IContextMenu帮忙的,所以答案还是在IContextMenu上,我们只要在调用GetUIObjectOf时把想查看的文件或文件件的PIDL做为参数传进去,然后直接调用InvokeCommand方法就OK啦。
当然,我们做的例子,还是弹出一个对象的属性,靠你自己修改了。
我们必须先得到 IContextMenu 接口:
//
得到 IContextMenu 接口
IntPtr iContextMenuPtr
=
IntPtr.Zero;
iContextMenuPtr
=
IParent.GetUIObjectOf(IntPtr.Zero, (
uint
)pidls.Length,
pidls,
ref
Guids.IID_IContextMenu,
out
iContextMenuPtr);
IContextMenu iContextMenu
=
(IContextMenu)Marshal.GetObjectForIUnknown(iContextMenuPtr);
但我们不弹出这个菜单,仅仅是调用 InvokeCommand 来执行命令而已:
//
直接执行命令
CMINVOKECOMMANDINFOEX invoke
=
new
CMINVOKECOMMANDINFOEX();
invoke.cbSize
=
Marshal.SizeOf(
typeof
(CMINVOKECOMMANDINFOEX));
invoke.lpVerb
=
Marshal.StringToHGlobalAnsi(
"
properties
"
);
invoke.lpDirectory
=
string
.Empty;
invoke.fMask
=
0
;
invoke.nShow
=
1
;
iContextMenu.InvokeCommand(
ref
invoke);
关于verb的更多信息请参考MSDN。我这里做的是执行“属性”,如果你要执行其他命令,或者按照索引来执行,也是可以的。这里不做深入研究。
![](https://i-blog.csdnimg.cn/blog_migrate/4279c16674da4808734a1373006fa7e9.jpeg)
源代码:/Files/lemony/WinShell4.rar
我正在考虑下一节是讲图标,还是继续讲iContextMenu。大家也知道,某个文件的右键菜单里面,往往会有几个 winrar 的选项,还带着可爱的图标。
![](https://i-blog.csdnimg.cn/blog_migrate/272a4c48baad8c879c15dc7768384f0f.jpeg)
很有可能下一节就讲述如何在C#中也实现这样的效果哦。希望大家多多支持^_^。