Click here to Skip to main content
15,879,326 members
Articles / Desktop Programming / WPF

WPF Menu using ViewModel - Part 1

Rate me:
Please Sign up or sign in to vote.
4.63/5 (11 votes)
18 Jun 2009CPOL4 min read 79.4K   2.8K   29   9
Implementation of WPF menus using View Model approach

Introduction

This article is an attempt to visualize a WPF menu using the MVVM architecture.

Background

This article is inspired from the works of Josh Smith, mainly his implementation of the TreeView. It opened at least my eyes and made me realize that a monster like a Treeview in WPF can be easily overwhelmed using a ViewModel approach. If you have not gone through the article, please have a look as it is a must read for all MVVM aspirants. 

Implementation on WPF Menu

What I have tried to achieve is to reuse the implementation of Treeview that Josh had done on WPF menus. Realizing a Menu Item as a View Model makes the implementation far easier and also enables separation of concern. So what a View Model object generally encloses is the state of the model and the properties associated with the presentation layer like IsEnabled, Icon etc. Header of the Menu Item is bound to the mapping property in the View Model which is mapped to let's same a Name property in the model.

The ViewModels

Let’s have a look at the Project structure that I have maintained:

MenuSolution.jpg

A BaseViewModel class is maintained which has the implementation for INotifyPropertyChanged interface which enables to have the PropertyChanged. The PropertyChanged event notifies the UI about the changes happening to the property in the View Model.

C#
public class BaseViewModel : INotifyPropertyChanged
{
   #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary> <summary>
    /// Notifies on property changed.
    /// </summary> </summary>
    /// Name of the STR property.
    public void NotifyOnPropertyChanged(string strPropertyName)
    {
      if (PropertyChanged != null) 
      { 
         PropertyChanged(this, new PropertyChangedEventArgs strPropertyName));
      }
    }

    #endregion
}

Using the Code

It’s sixes raining at Twenty 20 and I'm a keen follower of the game whenever it happens. So my sample has an inclination towards the terminologies used in the game.

Let us see the Viewmodels that I have used to accomplish the task.

MenuItemViewModel

This one I have used as the base class for all the menu item viewmodels.

C#
public class MenuItemViewModel : BaseViewModel
{
    /// <summary>
    /// Initializes a new instance of the <see cref=""MenuItemViewModel"">   
    ///  class.
    /// </see></summary>
    /// The parent view model.
    public MenuItemViewModel(MenuItemViewModel parentViewModel)
    {
        ParentViewModel = parentViewModel;
        _childMenuItems = new ObservableCollection<menuitemviewmodel>();
    }

    private ObservableCollection<menuitemviewmodel> _childMenuItems;
    /// <summary>
    /// Gets the child menu items.
    /// </summary>
    /// <value>The child menu items.</value>
    public ObservableCollection<menuitemviewmodel> ChildMenuItems
    {
        get
        {
            return _childMenuItems;
        }
    }

    private string _header;
    /// <summary>
    /// Gets or sets the header.
    /// </summary>
    /// <value>The header.</value>
    public string Header
    {
        get
        {
            return _header;
        }
        set
        {
            _header = value; NotifyOnPropertyChanged("Header");
        }
    }

    /// <summary>
    /// Gets or sets the parent view model.
    /// </summary>
    /// <value>The parent view model.</value>
    public MenuItemViewModel ParentViewModel { get; set; }

    public virtual void LoadChildMenuItems()
    {
            
    }

    /// <summary>
    /// Gets or sets the image source.
    /// </summary>
    /// <value>The image source.</value>
    public object IconSource { get; set; }
}

The menuitemviewmodel has a virtual method LoadChildMenuItems which will be overridden and used to load child menuitems in the application. The menuitemviewmodel is also associated with a parent view model in case a parent exists. It has a Header property which is bound with the Header property of MenuItem, i.e., the presentation property. We can also have properties like IconSource in the menuitemviewmodel which will have a direct mapping to the Source Property of an Icon which can be used in a MenuItem.

PlayerViewModel

This viewmodel has a direct mapping to the Player business object and inherits MenuItemViewModel class.

C#
public class PlayerViewModel : MenuItemViewModel
{
        private Player _player;
        /// <summary>
        /// Initializes a new instance of the <see cref=""MenuItemViewModel""> class.
        /// </see></summary>
        /// The parent view model.
        public PlayerViewModel(TeamViewModel parentViewModel, Player player)
            : base(parentViewModel)
        {
            _player = player;
        }

        private string _playerName;
        /// <summary>
        /// Gets or sets the name of the player.
        /// </summary>
        /// <value>The name of the player.</value>
        public string PlayerName
        {
            get
            {
                return _player.PlayerName;
            }
            set
            {
                _player.PlayerName = value;      
                NotifyOnPropertyChanged("PlayerName");
            }
        }
}

Similarly we have Twenty20TeamsViewModel which maps to Twenty20Teams business objects and TeamViewModel mapping to Team class.

C#
public class SeparatorViewModel : MenuItemViewModel
{
      public SeparatorViewModel(MenuItemViewModel parentViewModel) : 
              base(parentViewModel)
      {

      }
}

SeparatorViewModel

This viewmodel is very specific to the presentation part as it helps to show the separator object between menuitems.

How to use this viewmodel to enable separators will be discussed in the XAML part of the code which can be seen in my sample application.

A HierarchicalDataTemplate is defined whose DataType is set to MenuItemViewModel and ItemSource is set to the ChildMenuItems. So this will behave in a recursive way until the ChildMenuItems exist, that many levels of hierarchical structure of menus will be generated. So this will be a good approach if the menus are generated dynamically and we are not sure about the level upto which the hierarchy can happen. I'm loading the Viewmodels on startup of the application something like this as follows:

C#
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        Twenty20Teams twenty20Teams = new Twenty20Teams();
        twenty20Teams.Teams = new List<team>();

        Team teamA = new Team() { TeamName = "Team A"};
        teamA.Players = new List<player>();

        Player player1 = new Player(teamA) { PlayerName = "Player A" };
        teamA.Players.Add(player1);
            
        /*…………………………………………………………………………………………………………………………………………..
    …………………………………………………………………………………………………………………………………………..*/
        Player player6 = new Player(teamB) { PlayerName = "Player F" };
        teamB.Players.Add(player6);

        twenty20Teams.Teams.Add(teamA);
        twenty20Teams.Teams.Add(teamB);

        Twenty20TeamsViewModel viewModel = new 
                       Twenty20TeamsViewModel(twenty20Teams);
        viewModel.Header = "Twenty 20 Teams";
        MenuSampleWindow sampleWindow = new MenuSampleWindow(viewModel);
        sampleWindow.Show();
    }
}

Here, Twenty20TeamsViewModel is passed as a parameter to the constructor of the sample window. This viewmodel is set as the DataContext of the window.

C#
public MenuSampleWindow(MenuItemViewModel menuItemViewModel)
{
    InitializeComponent();
    this.DataContext = menuItemViewModel;
}

The Output

Output shows you the hierarchy of menu items being loaded and also the Separator items.

MenuItemViewModelSample

Points of Interest

Funny thing to be noticed here is that the Separator is a focussable item even if we have set the Focussable property to false. Still thinking what it is that I'm missing here to make it non focussable.

Conclusion 

Thinking in terms of ViewModels doesn't come by itself. It has to be cultivated within. Special thanks to Josh for the Treeview article which made a paradigm shift to my thought process. Hopefully my article will help someone to think in terms of view models.

History  

  1. How to use an XDocument along with Lambda expression and Linq for the sample application is updated in my blog site. Once the implementation is complete, it'll be available for download here.
  2. Updates related to this article is now available in by blogspot site (http://mywpf-visu.blogspot.com/)

License

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


Written By
Software Developer (Senior) Multi National
India India
I'm a C# developer with a multinational company. Recently started working on wpf and i'm hooked to it completely now.

Comments and Discussions

 
Questioncan not create another node of menu Pin
sachin01234516-Jun-16 4:46
sachin01234516-Jun-16 4:46 
GeneralSolution to Separator Focus problem Pin
Visu.v30-Jun-09 21:10
Visu.v30-Jun-09 21:10 
GeneralYou got my 4 Pin
FantasticFiasco17-Jun-09 21:48
FantasticFiasco17-Jun-09 21:48 
GeneralRe: You got my 4 Pin
Visu.v17-Jun-09 22:48
Visu.v17-Jun-09 22:48 
GeneralRe: You got my 4 Pin
FantasticFiasco18-Jun-09 0:04
FantasticFiasco18-Jun-09 0:04 
GeneralNice start Pin
Josh Smith16-Jun-09 5:06
Josh Smith16-Jun-09 5:06 
GeneralRe: Nice start [modified] Pin
Visu.v16-Jun-09 6:23
Visu.v16-Jun-09 6:23 
GeneralRe: Nice start Pin
Kumsi Shobha16-Jun-09 19:31
Kumsi Shobha16-Jun-09 19:31 
Good job Visu Smile | :) . This article is very useful for the beginers. Also explain how the menu items links to other forms.
Expecting more in the next article Big Grin | :-D

kumsi

GeneralRe: Nice start Pin
Visu.v16-Jun-09 19:58
Visu.v16-Jun-09 19:58 

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.