Saturday, September 8, 2012

WP Context Menu using MVVM

When I used ContextMenu in my application for the very first time, my applications were not using MVVM. Therefore, I had to add a context menu either in code behind or using Event Handler in code behind. Now, there were two issues in doing so :

1. Tedious Code Structure :  While adding a context menu from code behind and managing events.
2. Style Can't be saved to Resource Dictionary : While using Event Handler in xaml you can't define a style in Resource Dictionary because they do not have a code behind file. So you must define the style in your view only. It restrict the modularity of your application.

You can find many good articles about using ContextMenu in code behind. But problem still remains the same. So now how to solve this in more elegant way? And the answer comes with MVVM

For this article of mine I am using Galasoft-MVVM Light. A handful of useful links: 
  1. Download Galasoft-MVVM Light from here
  2. Alternatively, you can install it from NuGet (VS extension). 
  3. Learn how to install NuGet package from here
I am presuming you may have a little understanding of Galasoft's MVVM Light or any other MVVM framework, to better understand this article. 

Prerequisite (installed): 
1. NuGet extension for VS.
3. Galasoft-MVVM Light.

1. Objective: 

We will try to create a ListBox and fill it with some text item(s).  Each item in ListBox should have context menu say "READ" on click of that MenuItem we should be able to display the value of selected ListBox item.

2. Create a New Project: 

Create a new Project -> Silverlight for Windows Phone -> Mvvm and name it ContextMenu_MVVM.
Note:- once you have MVVM installed a default template will be available in Installed Templates as shown below. 



















3. Adding a Resource Dictionary (Style):

Open your project in Expression Blend. Once it's opened in Blend, right click project and Select Add new item





















Select Resource Dictionary and rename it Styles.xaml(Refer image above).

Now replace the code of Styles.xaml with this code:



 
 

This code contains a necessary style for a ListboxItem, that we are going to use for our demonstration purpose. 

4. Adding Code For MainPage.xaml 

In you MainPage.xaml replace code for ContentPanel grid with this one:


  
   
  


5. Update Code for MainPage View Model: 

In you MainViewModel.cs add this code in using syntax area to include the reference for some namespaces:

using GalaSoft.MvvmLight.Command;
using System.Collections.ObjectModel;
using System.Windows;


Add this code to same page just before you MainViewModel class closes:

#region Properties
public RelayCommand CTest { get; private set; }

private ObservableCollection _CtxItems;
public ObservableCollection CtxItems
{
    get
    { return _CtxItems; }
    set
    {
        if (_CtxItems == value)
        {
            return;
        }
        var oldValue = _CtxItems;
        _CtxItems = value;
        base.RaisePropertyChanged("CtxItems");
    }
}

private string _SelectedCtxItems;
public string SelectedCtxItems
{
    get
    { return _SelectedCtxItems; }
    set
    {
        if (_SelectedCtxItems == value)
        {
            return;
        }
        var oldValue = _SelectedCtxItems;
        _SelectedCtxItems = value;
        base.RaisePropertyChanged("SelectedCtxItems");
    }
} 
#endregion

#region Methods
private void LoadData()
{
    CtxItems = new ObservableCollection();
    for (int i = 0; i < 10; i++)
    {
        CtxItems.Add("Item " + i.ToString());
    }
}

private void Test(string data)
{
    MessageBox.Show(data);
}
#endregion

This code contains all the logic for populating your ListBox and RelayCommands used for doing command binding.

Add following code to MaiViewModel constructor to initialize the collection that we placed in code above.

public MainViewModel()
{
    if (IsInDesignMode)
    {
        // Code runs in Blend --> create design time data.
    }
    else
    {
        LoadData();
        CTest = new RelayCommand(Test);
    }
}

6. Working :

Style:

A style for ListBox item is defined in styles.xaml which contains a Textbox which has a ContextMenu. For this ContextMenu we have bound its property called Command to Relay Command i.e. CTest in MainViewModel. Also we are binding CommandParameter property of ContextMenu with Templated DataContext to insure we get currently selected item in our view-model. 

ViewModel: 

It contains 4 items:
  1. CTest: A property that will expose a relay command to enable binding to our view. 
  2. CtxItems: A property that provides ann ObservableCollection that contains items to be listed in ListBox. 
  3. LoadData: Methods to initialize CtxItems. 
  4. Test: An Action that need to be passed in as an argument for relay command while initializing command. 
A full source code link is given below for reference.

Source Code

Thanks and Regards
Nishant Rana