(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

MVVM in Windows 8: Part 2

(5 votes)
Gill Cleeren
>
Gill Cleeren
Joined Apr 02, 2010
Articles:   63
Comments:   6
More Articles
0 comments   /   posted on Jan 11, 2013
Tags:   mvvm , windows-8 , gill-cleeren
Categories:   Windows 8
Tweet

In this second part of our exploration of MVVM for Windows 8 Store applications, we are finally going to look at some code! The base concepts of the MVVM pattern were explained in part 1, I recommend that you read up on that part before diving in this part so that you certainly understand what the design goals are for using MVVM to build a Windows 8 Store app.

Implementing the application based on the MVVM pattern

Don't miss

Connecting Windows 8 apps with services

     All SilverlightShow Ebooks 

In the previous part, I’ve shown you the basic structure of the application. In this part, we’re going to explore the code.

The ViewModels

We’re going to start by looking in more detail at the ToDoMvvm.ViewModels. This project contains the ViewModels for the different views. Just so you remember correctly, the application contains 2 views: the view that contains a list of all ToDo items and a detail view.

clip_image002

clip_image004

For each of these views (we’ll look at the Views in just a minute), I’ve created a ViewModel. I often get asked if every view should map directly to one ViewModel. The answer is, as always, “It Depends”. Let me explain. In most cases, you’ll have one ViewModel for each View. However, sometimes you’ll end up with more complex views; in that case, you may decide that you’ll split up your View in different user controls. Then you could create a separate ViewModel for each user control. But technically, nothing is holding you from creating just one large ViewModel that manages all user control’s views/viewmodels.

On the other hand, you may have a wizard-like view. In this case, you may have one ViewModel that manages the other ViewModels. In short, there’s no general rule, but there’s no right or wrong here either.

Now for our application at hand here, we just have two ViewModels named MainViewModel and EditViewModel.

clip_image005

Notice in the References list, there’s also a reference to the MvvmLight libraries listed. These were added using Nuget, the Visual Studio Package Manager. You can find MvvmLight on Nuget as follows:

  • Right-click on the References node and select “Manage NuGet Packages”. This will open the NuGet dialog.
  • In the dialog, search for MvvmLight.

clip_image007

  • Click install, accept the license and watch Visual Studio set all the references correct for you.

Below you can see the relevant part of the MainViewModel (we’ll look at more parts of it later on).

public class MainViewModel : ViewModelBase, IMainViewModel
{
    private IToDoDataService toDoDataService;
    private INavigationService navigationService;
    public RelayCommand<ToDoItem> ItemClickCommand { get; set; }
    private string title;
    public string Title
    {
        get { return title; }
        set
        {
            title = value;
            RaisePropertyChanged("Title");
        }
    }
    private ToDoItem selectedToDo;
    public ToDoItem SelectedToDo
    {
        get
        {
            return selectedToDo;
        }
        set
        {
            selectedToDo = value;
            RaisePropertyChanged("SelectedToDo");
        }
    }
    private ObservableCollection<ToDoItem> todoItems;
    public ObservableCollection<ToDoItem> ToDoItems
    {
        get
        {
            return todoItems;
        }
        set
        {
            todoItems = value;
            RaisePropertyChanged("ToDoItems");
        }
    }
    public MainViewModel()
    {
        Title = "Hello world";
        toDoDataService = SimpleIoc.Default.GetInstance<IToDoDataService>();
        navigationService = SimpleIoc.Default.GetInstance<INavigationService>();
        LoadAllToDos();
    }
    public void Initialize()
    {
    }
    public async void LoadAllToDos()
    {
        ToDoItems = await toDoDataService.GetAllToDoItems();
    }
}

The class itself is inheriting from ViewModelBase and implements IMainViewModel (defined in the Contracts project). ViewModelBase is a base class that comes with MVVM Light. As with everything in MVVM Light, it’s a helper class that already contains a couple of things to get you on your way more easily. For example, we can use the RaisePropertyChanged method instead of raising the PropertyChanged event of the INotifyPropertyChanged interface. The ViewModelBase class definition is shown below.

    // Summary:
    //     A base class for the ViewModel classes in the MVVM pattern.
    public abstract class ViewModelBase : ObservableObject, ICleanup
    {
        public ViewModelBase();
        public ViewModelBase(IMessenger messenger);
        public bool IsInDesignMode { get; }
        public static bool IsInDesignModeStatic { get; }
        protected IMessenger MessengerInstance { get; set; }
        protected virtual void Broadcast<T>(T oldValue, T newValue, string propertyName);
        public virtual void Cleanup();
        protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression, T oldValue, T newValue, bool broadcast);
        protected virtual void RaisePropertyChanged<T>(string propertyName, T oldValue, T newValue, bool broadcast);
        protected bool Set<T>(Expression<Func<T>> propertyExpression, ref T field, T newValue, bool broadcast);
        protected bool Set<T>(string propertyName, ref T field, T newValue, bool broadcast);
    }

In the MainViewModel, we are exposing state that the view can bind on. In this case, this state consists out of the SelectedToDo and the ToDoItems (ObservableCollection). Also, in the constructor, we are instantiating the ITodoDataService instance using an IOC (Inversion-Of-Control) container. We’ll come back to that later on. In any case, we’re calling the LoadAllToDos. This method, part of the ViewModel, asks the Model to asynchronously load the data from the service. In our case, the service is acting as interface for the data access. Notice that for the ViewModel, this way of working is beneficial when the ViewModel needs to be tested: we can easily replace the service with a mock implementation, therefore enabling to test it in isolation.

 
public async void LoadAllToDos()
{
    ToDoItems = await toDoDataService.GetAllToDoItems();
}
Below is the GetAllToDoItems method implementation of the ToDoDataService. This method performs the actual WCF Service call. But since we have this abstraction layer on top, our ViewModel doesn’t contain a hard reference to this WCF service. Loose coupling is all around!
public async Task<ObservableCollection<ToDoItem>> GetAllToDoItems()
{
    ToDoServiceClient client = new ToDoServiceClient();
    var result= await client.GetAllToDosAsync();
    return result;
}

The EditViewModel is pretty similar, we’re not going to spend time on that for now. Feel free to take a look at its implementation at this point. We’ll be coming back to the ViewModels again when we add messaging.

The Views

Now that we have implemented the ViewModels, we should take a look at the Views. The project ToDoMvvm has the following references:

clip_image008

Notice that this project also references the MVVM Light assemblies. You’ll need to repeat the NuGet procedure as explained before to add these references.

As mentioned before, the Views are binding on the state exposed by the ViewModels. Below is the XAML code for the MainPage.xaml.

<Page
    x:Class="ToDoMvvm.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ToDoMvvm"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Win8nl_Behavior="using:Win8nl.Behaviors"
    DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}"
    xmlns:WinRtBehaviors="using:WinRtBehaviors"
    mc:Ignorable="d">
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel>
            <TextBlock Text="Windows 8 TODO app" FontSize="42" Margin="20"></TextBlock>
            <ListBox ItemsSource="{Binding ToDoItems}" Height="300" Name="MyListBox" 
                     SelectedItem="{Binding SelectedToDo, Mode=TwoWay}" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Title}"></TextBlock>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </Grid>
</Page>

Notice the ListBox. It’s binding to ToDoItems (hopefully, you’ll remember that this is the ObservableCollection exposed by the MainViewModel). But… we are missing something. Where is the source for the binding coming from? The View needs a reference to the ViewModel to be able to bind. For this, we are using the Locator pattern. A locator is a class that helps other classes to locate the instances they need. In this project, we’ve added a ViewModelLocator as a class that contains a static reference for all ViewModels that are available in the application. The code for this ViewModelLocator is shown below.

public class ViewModelLocator
{
    /// <summary>
    /// Initializes a new instance of the ViewModelLocator class.
    /// </summary>
    public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
        SimpleIoc.Default.Register<INavigationService, NavigationService>();
        SimpleIoc.Default.Register<IToDoDataService, ToDoDataService>();
        SimpleIoc.Default.Register<IDialogService, DialogService>();
        SimpleIoc.Default.Register<IShareService, ShareService>();
        SimpleIoc.Default.Register<IMainPage, MainPage>();
        SimpleIoc.Default.Register<IEditPage, EditPage>();
        SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(true);
        SimpleIoc.Default.Register<IEditViewModel, EditViewModel>(true);
    }
    public MainViewModel MainViewModel
    {
        get
        {
            return ServiceLocator.Current.GetInstance<IMainViewModel>() as MainViewModel;
        }
    }
    public EditViewModel EditViewModel
    {
        get
        {
            return ServiceLocator.Current.GetInstance<IEditViewModel>() as EditViewModel;
        }
    }
    public static void Cleanup()
    {
        // TODO Clear the ViewModels
    }
}

This code requires some explanation! In the constructor, we are using an IOC container. In this case, we are using SimpleIOC, a very basic IOC container that comes with MVVM Light. Of course, you can change this to be your own container of choice. We are registering the two ViewModels we have with this container using their interface.

SimpleIoc.Default.Register<IMainViewModel, MainViewModel>(true);
SimpleIoc.Default.Register<IEditViewModel, EditViewModel>(true);

Using the container, we are not creating the instances ourselves. We are saying to the container: “When someone asks you to return an instance of IMainViewModel, give them an MainViewModel”. In the two properties that are declared on this ViewModelLocator, we are using the ServiceLocator to return an instance of the IMainViewModel (or IEditViewModel).

 
public MainViewModel MainViewModel
{
    get
    {
        return ServiceLocator.Current.GetInstance<IMainViewModel>() as MainViewModel;
    }
}
public EditViewModel EditViewModel
{
    get
    {
        return ServiceLocator.Current.GetInstance<IEditViewModel>() as EditViewModel;
    }
}
We need to declare an instance of the ViewModelLocator. I’m doing that in the App.xaml as follows:
<vm:ViewModelLocator x:Key="Locator" />

With this instance declared, my View can access its ViewModel by adding the following code:

DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}"  

If we take a quick look at the code-behind for the MainPage, we can see this is mainly empty:

public sealed partial class MainPage : Page, IMainPage
{
    public MainPage()
    {
        this.InitializeComponent();
    }
    /// <summary>
    /// Invoked when this page is about to be displayed in a Frame.
    /// </summary>
    /// <param name="e">Event data that describes how this page was reached.  The Parameter
    /// property is typically used to configure the page.</param>
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
    }
    public IViewModel ViewModel
    {
        get { return this.DataContext as IMainViewModel; }
    }
}

The class implements IMainPage, another interface I’ve declared in the Contracts project. Take another look at the code for the IOC container: IMainPage and the implementation MainPage are registered with the container there as well.

Commands

We have now seen that we can bind to state exposed by the ViewModel in the View. In Part 1, I’ve said that the ViewModel contains most of the functionality previously available in the code-behind. This means that code that originally was available in event handlers, should be moved to the ViewModel code base as well. But of course, there should be no direct link between the two. So how can we fix this?

The answer is commands. A command exposed on the ViewModel can be bound to in the View as well. Commands are available through the ICommand interface in WinRT. The following code shows this interface:

    
public interface ICommand    
{        
    event EventHandler CanExecuteChanged;       
    bool CanExecute(object parameter);        
    void Execute(object parameter);    
}

The Execute() method is the most important one, it will be called when the event is happening in the UI. The CanExecute() method is used to check if a command can execute. The CanExecuteChanged event is raised when the CanExecute is changing and therefore should be checked again.

We can of course implement the interface ourselves, but MVVM Light comes to the rescue again. In the API, the RelayCommand is available. In the MainViewModel, the following RelayCommand is added that will handle the clicking on an item in the ListBox:

public RelayCommand<ToDoItem> ItemClickCommand { get; set; }

In the constructor, I’ve added the InitializeCommands() method call:

public MainViewModel()        
{            
    ...            
    InitializeCommands();            
    ...        
}

The implementation for this method is shown below.

private void InitializeCommands()        
{            
    ItemClickCommand = new RelayCommand<ToDoItem>((item) =>            
    {                 
        ...            
    });        
}

In this method, we are instantiating the RelayCommand using a lambda expression. We’ll add the code for this later on (in the next part when we look at messaging).

In the UI, we can now bind to these commands. However, not every control exposes a Command property. In Silverlight, this could be fixed using the EventToCommand. However, that doesn’t work in Windows 8, so instead, I’m using another library available from NuGet: WinRtBehaviors.

In the XAML for the MainPage.xaml, we want to trigger the execution of the command when the user clicks on an item in the ListBox and therefore triggers the SelectionChanged event. Using WinRtBehaviors, this is possible as follows:

<ListBox ItemsSource="{Binding ToDoItems}" Height="300" Name="MyListBox" 
            SelectedItem="{Binding SelectedToDo, Mode=TwoWay}" >
    <WinRtBehaviors:Interaction.Behaviors>
        <Win8nl_Behavior:EventToCommandBehavior Event="SelectionChanged" 
                                                Command="ItemClickCommand" 
                                                CommandParameter="{Binding SelectedToDo, Mode=TwoWay}"/>
    </WinRtBehaviors:Interaction.Behaviors>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Title}"></TextBlock>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Notice that we are setting as the CommandParameter another Binding, in this case to SelectedToDo. This binding is a two-way binding. Let me explain what happens here.

When the user selects a value in the ListBox, we are setting (using a TwoWay binding) this value in the ViewModel. This selected value can then be accessed from the command code. In our case, we will use this (in the next article) to pass to the detail page which item was selected by the user so we can show the detail page with that item as the data context.

Summary

In this second part of this article series, we have seen how we can create our ViewModels and using the Locator pattern, how the View can locate its ViewModel without creating a hard link between the two. We’ve also covered Commands, which allow us to write code that is triggered by events in the UI, also in the ViewModel. For all this, we’ve been using MVVM Light again, since it provides us with a number of helper classes that gets us on the way much faster!

About the author

Gill Cleeren is Microsoft Regional Director (www.theregion.com), Silverlight MVP (former ASP.NET MVP) and Telerik MVP. He lives in Belgium where he works as .NET architect at Ordina (http://www.ordina.be/). Passionate about .NET, he’s always playing with the newest bits. In his role as Regional Director, Gill has given many sessions, webcasts and trainings on new as well as existing technologies, such as Silverlight, ASP.NET and WPF at conferences including TechEd Berlin 2010, TechDays Belgium – Switzerland - Sweden, DevDays NL, NDC Oslo Norway, SQL Server Saturday Switserland, Spring Conference UK, Silverlight Roadshow in Sweden, Telerik RoadShow UK… He’s also the author of many articles in various developer magazines and for SilverlightShow.net and he organizes the yearly Community Day event in Belgium. He also leads Visug (www.visug.be), the largest .NET user group in Belgium. Gill is the author of “Silverlight 4 Data and Services Cookbook”. In 2012, the second edition, “Silverlight 5 Data and Services Cookbook” was released.

You can find his blog at www.snowball.be.

Twitter: @gillcleeren


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series