Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a Dynamic WPF Menu: Part 1

0.00/5 (No votes)
24 Oct 2014 1  
Part 1 of how to create a dynamic WPF menu

WPF provides some really nice features allowing us to create dynamic user interfaces. In the next 3 posts, I will demonstrate how to create a dynamic menu bar [or tool-bar] that we can add and remove items to at run time. For brevity, I have limited the scope to exclude any real styling and have created a very simple example that can easily be extended. Where necessary, I will note some options for extending the menu bar to provide increased functionality.

To create a dynamic menu, we need the following:

  • A WPF form with a hierarchical data template to display the menu
  • A service to control the adding and removing of items on the menu
  • A way of standardizing all the menu items to ensure consistency

In this first post, I will cover the service controller and standardizing our menu items.

Firstly, the menu service. This needs to provide the ability to add and remove items to the menu, and to return our top-level items to the hierarchical data template. Out top-level items will be items like ‘File’, ‘Edit’, etc. In the code below, I have set the top-level items as a list. This means to make it dynamic once it has been created, you may want to replace the list with an observable collection (as sub-items will be) or if left as a list, ensure we notify any binding controls (in this case the hierarchical data template) of any changes by implementing the INotifyPropertyChanged interface in the view model. I have purposely omitted this to reduce code complexity, and in most cases, the top-level menu items will probably be fixed.

public class MenuService
    {
        private List<MenuItem> allMenuItems;

        public MenuService()
        {
            allMenuItems = new List<MenuItem>();
        }

        public List<MenuItem> GetParentMenuItems()
        {
            List<MenuItem> parentItems = allMenuItems.FindAll(x => x.Parent == null);

            return parentItems;
        }

        public void AddMenuItem(MenuItem item, string parentName = "")
        {
            if (parentName == string.Empty)
            {
                this.allMenuItems.Add(item);
            }
            else
            {
                MenuItem parent = allMenuItems.Find(x => x.Name == parentName);
                if (parent != null)
                {
                    item.Parent = parent;
                    parent.AddSubMenu(item);
                }
                allMenuItems.Add(item);
            }
        }

        public void RemoveMenuItem(MenuItem menuItem)
        {
            foreach (MenuItem item in allMenuItems)
            {
                item.RemoveSubMenu(menuItem);                
            }

            if (this.allMenuItems.Contains(menuItem))
            {
                this.allMenuItems.Remove(menuItem);
            }
        }
    }

Now to standardize the menu items so we can bind to them. Generally, there are two options for this, and interface or an abstract class. For this situation, I have opted to use an abstract class, as most of the functionality will be shared across all menu items. The only thing we need to set in each menu are the values to return (name, text to display) and what we will do when we click the menu item. There will also be the optional extra of being able to set the menu item as enabled or disabled. Below is the abstract base MenuItem class:

public abstract class MenuItem
    {
        private string name;

        private string text;

        private ObservableCollection<MenuItem> subItems;

        private ICommand onSelected;

        private MenuItem parent;

        public MenuItem(string name, string text)
        {
            this.name = name;
            this.text = text;
            this.subItems = new ObservableCollection<MenuItem>();
        }

        public string Name { get { return this.name; } }

        public string Text { get { return this.text; } }

        public MenuItem Parent { get { return this.parent; } set { this.parent = value; } }

        public ICommand OnSelected
        {
            get
            {
                if (this.onSelected == null)
                {
                    this.onSelected = new MenuCommand(this.OnItemSelected, this.ItemCanBeSelected);
                }
                return this.onSelected;
            }
        }

        public ObservableCollection<MenuItem> SubItems
        {
            get
            {
                return this.subItems;
            }
        }

        public void AddSubMenu(MenuItem menuItem)
        {
            this.subItems.Add(menuItem);
        }

        public void RemoveSubMenu(MenuItem menuItem)
        {
            if(this.subItems.Contains(menuItem))
            {
                this.subItems.Remove(menuItem);
            }
        }

        public abstract void OnItemSelected();

        public virtual bool ItemCanBeSelected()
        {
            return true;
        }
    }

In the next part, I will cover the implementation of ICommand and the hierarchical data template.

Filed under: C#, CodeProject, Software, WPF
Tagged: C#, HierarchicalDataTemplate, ICommand, menu, MVVM, WPF, XAML

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here