Click here to Skip to main content
15,902,778 members
Articles / Desktop Programming / WPF

Hierarchical Menu in WPF

Rate me:
Please Sign up or sign in to vote.
4.86/5 (7 votes)
30 Jul 2010CPOL2 min read 40.5K   950   24   6
A simple way to create a hierarchical context menu.

Introduction

At least twice in my programming career, I've had the need to create a hierarchical menu. In this example, I will show how to do a hierarchical context sensitive menu with very simple code, almost ridiculously simple. I searched some solutions on the net, and they involved quite complex code for finding the children in real time when the item was opened, converters, etc., and there was a huge amount of code to accomplish such a simple task.

menu1.jpg

Using the Code

Well, it boils down to having a hierarchical data structure that is to be put in the menu. If the data is fixed and known, it's just a matter of designing the menu in the designer, but if it is dynamic data, we have to implement some functionality to accomplish this.

A simple representation is a list with entries that has an ID, a parent ID, and a header name. Then I also add an item for the icon (if any), ImageFile, and if the entry should respond on mouse click ("HasEvent").

C#
public class MenuObj
{
    public int Id;
    public string Name;
    public int ParentId;
    public bool HasEvent;
    public string ImageFile;
}

The list in this example looks like this:

C#
this.ContextMenu = menu.GetMenu(new List<MenuObj> 
{ 
    new MenuObj() { Id = 1, Name = "Presence", ImageFile = "icon1.png"},
    new MenuObj() { Id = 2, Name = "Absence", ImageFile = "icon2.png"},
    new MenuObj() { Id = 3, Name = "Other", ImageFile = "icon1.png"},
    new MenuObj() { Id = 11, ParentId = 1, Name = "Work time" },
    new MenuObj() { Id = 12, ParentId = 1, Name = "Overtime" },
    new MenuObj() { Id = 13, ParentId = 1, Name = "Additional time" },
    new MenuObj() { Id = 21, ParentId = 2, Name = "Sickleave" },
    new MenuObj() { Id = 22, ParentId = 2, Name = "Parentleave" },
    new MenuObj() { Id = 23, ParentId = 2, Name = "Flex" },
    new MenuObj() { Id = 24, ParentId = 2, Name = "Duty off" },
    new MenuObj() { Id = 31, ParentId = 3, Name = "Miles" },
    new MenuObj() { Id = 32, ParentId = 3, Name = "Bonus" },
    new MenuObj() { Id = 111, ParentId = 11, Name = "Project 1", HasEvent = true },
    new MenuObj() { Id = 112, ParentId = 11, Name = "Project 2", HasEvent = true },
    new MenuObj() { Id = 113, ParentId = 11, Name = "Project 3", HasEvent = true }
});

Let's now create a class that creates a context menu with these items, where ParentId = 0 means that items belong to the root, and other parentIds reference the items above.

C#
public class HCMenu
{
    RoutedEventHandler MouseDownEventHandler;

    public HCMenu(RoutedEventHandler MouseDownEventHandler)
    {
        this.MouseDownEventHandler = MouseDownEventHandler;
    }

    public ContextMenu GetMenu(List<MenuObj> MenuObjects)
    {
        ContextMenu cm = new ContextMenu();
        Hashtable MenuObjs = new Hashtable();
        MenuObjs[0] = cm;
        
        foreach (MenuObj mo in MenuObjects)
        {
            MenuItem mi = new MenuItem();
            mi.Header = mo.Name;
            mi.Tag = mo.Id;

            if (mo.ImageFile != null) 
            {
                mi.Icon = new Image() {Source = new BitmapImage(
                          new Uri(mo.ImageFile, UriKind.Relative)), Width=22};
            }

            if (mo.HasEvent) mi.Click += MouseDownEventHandler;

            MenuObjs[mo.Id] = mi;

            if (mo.ParentId == 0)
            {
                (MenuObjs[mo.ParentId] as ContextMenu).Items.Add(mi);
            }
            else
            {
                (MenuObjs[mo.ParentId] as MenuItem).Items.Add(mi);
            }
        }

        return cm;
    }
}

In the code above, we start iterating trough the menu items (supposed to be sorted from lower to higher IDs). We save the items we create in a Hashtable for convenience, and add a certain item to its parent item (that we access through the hashtable). Study the code a little, and you will understand how simple it works.

In the constructor of the menu, we also send a RoutedEventHandler to the mouse click event in the menu. These events should be in the main class. Note the e.Handled = true; in the end! Very important. I discovered that if you don't stop the routing, the parental items will also raise the mouse click event until it reaches the root.. We don't want that, we want the one we clicked at to be the only one raising the event.

C#
void MenuClicked(object sender, RoutedEventArgs e)
{
    int Id = (int)((sender as MenuItem).Tag);

    switch (Id)
    {
        case 111: MessageBox.Show("Do something with item 111"); break;
        case 112: MessageBox.Show("Do something with item 112"); break;
        default: MessageBox.Show("Do something with item "+Id); break;
    }

    e.Handled = true;
}

I hope this will save you some trouble in making doing a task quickly and efficiently.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer HRM Software AB
Sweden Sweden
Jonas Wranå, software developer from Sweden, currently working at HRM Software AB, a company that develop a major salary and resource planning system. Some other interests includes mathematics, fractal geometry, pedagogy, music composition and photography.

Comments and Discussions

 
Questionseparator ? Pin
Member 112698391-Feb-19 5:11
Member 112698391-Feb-19 5:11 
GeneralMy vote of 5 Pin
koolprasad20033-Aug-10 4:12
professionalkoolprasad20033-Aug-10 4:12 
GeneralI would normally just do this Pin
Sacha Barber27-Jul-10 21:40
Sacha Barber27-Jul-10 21:40 
GeneralRe: I would normally just do this Pin
Jonas Wranå28-Jul-10 1:00
Jonas Wranå28-Jul-10 1:00 
GeneralMy vote of 5 Pin
sam.hill26-Jul-10 6:12
sam.hill26-Jul-10 6:12 
GeneralRe: My vote of 5 Pin
Jonas Wranå28-Jul-10 5:04
Jonas Wranå28-Jul-10 5:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.