Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / system

Using DirectoryInfoEx to Simplify ContextMenu Construction

0.00/5 (No votes)
24 Oct 2010LGPL32 min read 10.1K   112  
This post explains how to use ContextMenuHelperEx in DirectoryInfoEx to simplify ContextMenu construction under .NET 4.0 Framework.

ContextMenu2.png

This article explains how to use ContextMenuHelperEx in DirectoryInfoEx to simplify ContextMenu construction under .NET 4.0 Framework.

Introduction

DirectoryInfoEx includes a class named ContextMenuHelperEx, which can create menu items using ShellAPI like InsertMenuItem() and SetMenuItemBitmap(). It’s originally designed for use in a Window Forms or WPF application.

As .NET 4.0 Framework allows running multiple runtime in a process, constructing context menu is now possible. All-In-One Code Framework included a demo (CSShellExtContextMenuHandler), it works great, except the demo doesn't provide sample for icon and cascade menu, and it will be difficult to maintain as the menu grows.

So instead of writing it all over again, I found it is easier to use the ContextMenuHelperEx to do the work.

ContextMenuHelperEx includes a method named ConstructCustomMenu():

C#
public static void ConstructCustomMenu(IntPtr menu, CustomMenuStructure[] customItems,
uint idCmdFirst, out List<IntPtr> menuPtrConstructed, out List<IntPtr> imgPtrConstructed)

ConstructCustomMenu() method takes a CustomMenuStructure, which is an ISerializable that can be stored in harddrive using BinaryFormatter/XmlFormatter. I have included a very basic editor for this.

ContextMenuEditor.png

CustomMenuStructure includes Text , ToolTip, ID, Command, Checked, Items, Icon property.

  • Text property specifies the header of the menuitem.
  • ID identifies the item in the menu.
  • Checked specifies to display checked marks or not.
  • Items specifies the sub items.
  • Icon, if not null, stores the Bitmap of the menu. (16×16)
  • ToolTip and Command are ignored by the ConstructCustomMenu() command.

ConstructCustomMenu() returns the pointer of Sub menu and Icon’s HBitmap constructed, when they are no longer required, they should to be freed DestroyMenu() and DeleteObject().

How to Use?

You should read the readme.txt in the CSShellExtContextMenuHandler demo first.

C#
public int QueryContextMenu(IntPtr hMenu, uint iMenu, uint idCmdFirst,
   uint idCmdLast, uint uFlags)
{
     cmsRoot = new CustomMenuStructure("Hello", 0);
     cmsRoot.Icon = new Bitmap(
      this.GetType().Assembly.GetManifestResourceStream("TestContextMenuProj.Bitmap1.bmp")
      as Stream);
     cmsRoot.Items.Add(new CustomMenuStructure("World", 1));

     IDLookupDic.Add(0, cmsRoot);
     IDLookupDic.Add(1, cmsRoot.Items[0]);

     //using (FileStream fs = File.OpenRead(fileName)) //Or load from binary file
     //{
     //       BinaryFormatter bformatter = new BinaryFormatter();
     //       cmsRoot = (CustomMenuStructure)bformatter.Deserialize(fs);
     //}

     ContextMenuHelperEx.ConstructCustomMenu(hMenu,
            new CustomMenuStructure[] { cmsRoot }, idCmdFirst,
            out menuPtrConstructed, out imgPtrConstructed);

     return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0,
            (uint)IDLookupDic.Count + 1);
}

In QueryContextMenu, you have to prepare the CustomMenuStruture, either load from a binary file, or hardcode it. IDLookupDic is a dictionary that holds the structures indexed by ID.

C#
public void GetCommandString(UIntPtr idCmd, uint uFlags, IntPtr pReserved,
    StringBuilder pszName, uint cchMax)
{
    uint id = idCmd.ToUInt32();
    if (IDLookupDic.ContainsKey(id) &&
           !String.IsNullOrEmpty(IDLookupDic[id].Tooltip))
     {
        switch ((GCS)uFlags)
        {
            case GCS.GCS_HELPTEXTW:
                 if (this.IDLookupDic[id].Tooltip.Length > cchMax - 1)
                 {
                    Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER);
                 }
                 else
                 {
                    pszName.Clear();
                    pszName.Append(this.IDLookupDic[id].Tooltip);
                 }
                 break;
        }
   }
}

This is not compulsory to implement, you can return help text based on the requested id.

C#
public void InvokeCommand(IntPtr pici)
{
    CMINVOKECOMMANDINFO ici = (CMINVOKECOMMANDINFO)Marshal.PtrToStructure(pici,
          typeof(CMINVOKECOMMANDINFO));
    uint id = (uint)(NativeMethods.LowWord(ici.lpVerb.ToInt32()));
    if (IDLookupDic.ContainsKey(id))
    {
        saveDroppedFileList();
        //Execute your application...
    }
    else
        Marshal.ThrowExceptionForHR(WinError.E_FAIL); //not recognized
}

The saveDroppedFileList() method saves the selected file names to a temporary file, and passes the temporary file name instead of all selected file names. You can then execute your main application using Process.Start() method.

Reference

This article has been posted on . You can find a list of my articles here.

Image 3

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)