Click here to Skip to main content
Click here to Skip to main content

WPF Menu using ViewModel - Part 1

By , 18 Jun 2009
 

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.

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.

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.

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.

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:

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.

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)

About the Author

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

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralSolution to Separator Focus problemmemberVisu.v30 Jun '09 - 21:10 
I got a solution to the problem of Separator having focus in WPF menu if it is a bound one, thanks to one of colleagues effort. The same solution also applied to a problem of a disabled menu item also having focus. Some tweaks related to "StaysOpenOnClick" property of MenuItem also helped. Check out my blog site for the same (Remove focus from Menu Separator[^])
GeneralYou got my 4memberdisore17 Jun '09 - 21:48 
I really like MVVM and find your article interesting. I'm not giving it a 5 since it is a relatively basic design at this point, but am looking forward to next article.
 
Why is it drug addicts and computer afficionados are both called users?
--Clifford Stoll

GeneralRe: You got my 4memberVisu.v17 Jun '09 - 22:48 
Thanks a lot Disore.
Still working on the app for my next article which i hope will be an upgrade from the version available. Also you can check out my blog http://mywpf-visu.blogspot.com/[^]which is having all the where there are some updates about the implementation available to be released soon.
 
I had loved your article "Showing Dialogs When Using MVVM Pattern". I had implemented my own version of having Dialogs using MVVM before i had read your article. Maybe i can share my own version in my blog or as an article.
 
cheers
 
Visu
 
http://mywpf-visu.blogspot.com[^]
GeneralRe: You got my 4memberdisore18 Jun '09 - 0:04 
Definitely, I would love to get your angle on how to solve the problem. Creating a discussion on the problem was the main point of the article, not the code itself.
 
Why is it drug addicts and computer afficionados are both called users?
--Clifford Stoll

GeneralNice startmvpJosh Smith16 Jun '09 - 5:06 
Thanks for the article, Visu. It's a good start. It would be useful if you showed the reader how the ViewModel objects respond to the user clicking on a MenuItem.
 
:josh:
Try Crack![^]
Sleep is overrated.

GeneralRe: Nice start [modified]memberVisu.v16 Jun '09 - 6:23 
Great to hear from you Josh. Thanks for the feedback. I'm planning for the Part 2 pretty soon which will have Commanding, styling and some other stuffs. Will be available initially in my blog site http://mywpf-visu.blogspot.com/[^]
 
modified on Tuesday, June 16, 2009 2:04 PM

GeneralRe: Nice startmemberKumsi 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 startmemberVisu.v16 Jun '09 - 19:58 
Thanks a Lot Shoba. gr8 to hear that it's helping. You can expect more in the articles which are expected to follow soon.

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 19 Jun 2009
Article Copyright 2009 by Visu.v
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid