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

MVVM for Windows Phone Developers–Use it Guys! (Part 3)

0.00/5 (No votes)
28 Aug 2013 1  
MVVM for Windows Phone developers.

Introduction

In the last part of this series we extended our solution with a PCL library for our models, view-models, and we added a Windows 8 project as well as a testing project for Windows 8. Preparing our projects to with the right version of MVVM Light.

We added a the NuGet package “MVVM Light libraries only (PCL)”. To install the same binary versions, and the specific platform libraries, we need to add the package to our Windows Phone 8 and our Windows 8 project. The installer will install the base-libraries and the specific libraries for Windows Phone 8 and Windows 8.

Tip: Somehow the portable package is only working, when adding the portable package of “BCL Portability Pack for .NET Framework3, Silverlight 4 and 5, And Windows Phone 7.5”. Just search for “Microsoft BCL” and install the package.

Architecture changes

Through the use of PCL, we have a major change in our architecture. All models, view-models and our DAL-Helpers will be located in our PCL library. Here is the changed diagram:

You see, that all the parts we need are now located in the PCL library. Through this architectural change, we need to handle now other problems, that come up with PCL.

We can use a “centralized” view-model-locator, that will help us, to locate our view-models via static-properties, or should we try another approach?

And some other questions that come up like: “How can we manage the data-access in a generic way?”.

Solution

Implement a bootstrapper for MVVM-Light with Ninject

Ok. We have certain types that are implemented by our view-models and model classes. We have also interfaces that are implemented by our units-of-work and our repositories. This is a very good use-case for dependency injection. We roll up everything bottom-up based on our architecture diagram.

Adding the IUnit of work interface

Expand our MVVMWindowsPhone.Core.Portable (maybe this should be renamed) and right-click the folder DAL. Select “Add new item…” and add a new interface, name it IUnitOfWork. Add the following code:

using System;using System.Collections.Generic; 
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace MVVMWindowsPhone.Core.Portable.DAL
{
//
// Unit of work to use as abstraction.
//
//
public interface IUnitOfWork where T:class
{
//
// The context to work with
// the specific database, file,
// whatever.
//
T Context {get; set;}
//
// Set the current context.
// Since we cannot use constructors
// in interfaces.
//
//
void SetContext(T context);
}
}

This interface has one property of type T called Context this will hold our “Database access object” like a context used for EF or the SQLiteConnection object, to perform actions to our databases. Every platform like Windows 8 or Windows Phone 8 will have it’s own implementation.

The next interface to add is the IRepository interface, it will be placed into the DAL folder also:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace MVVMWindowsPhone.Core.Portable.DAL
{
    public interface IRepository<t,u> where T:class where U:class
    {
        //
        // The unit of work to use.
        // Like SQLite, or file driver.
        //
        IUnitOfWork Driver {get; set;}</span>
        //
        // Get all entries.
        //
        //
        IQueryable GetAllEntries();
        //
        // Get filtered entries.
        //
        //
        //
        //
        IQueryable GetFilteredEntries(IEnumerable data,
                        Expression<func<t,bool>>

This interface has the following members:

  • IUnitOfWork => Our DB access object
  • IQueyable GetAllEntries() => Get all entries from a specific table, of file
  • IQueryable GetFilteredEntries(IEnumeralable data, Expression<func<t, bool=”">> filter) => allows us, to filter for specific values using LINQ
  • T DeleteEntry(T Entry) => allows us to delete a specific entry
  • T UpdateEntry(T Entry, T updateValue) => allows us to update a specific entry. This is not commonly used in an IRepository. If we would use EF (EntityFramework) we could ignore this member. But we are using SQLight, which still seems to have problems with atomic database actions.
  • T AddEntry(T Entry) => Add a new entry to a table or file, or whatever we use to save data (like a web-service for example)

Install Ninject for Portable Class Libraries

Ninject is also available for portable class libraries. So right-click the “References” entry in our portable-core project and choose “Manage NuGet-Packages…”. Type “ninject-portable” into the search-box on the right side and install the entry stating “Ninject for Portable Class Libraries”. That’s it.

NinjectPortable

The bootstrapper based on Ninject

To understand Ninject, and how it basically works, you can visit the Ninject Wiki here:

However, if you want to learn more about dependency injection using .NET you can buy this book:

The base requirements for the bootstrapper are:

  • Localize and create instances of the view-models we use
  • Inject all the interfaces and load the respective types, tied to those interfaces, or hosting the interfaces
  • Create a locator, that enables us to use the view-models in our applications on Windows Phone 8 and Windows 8

Preparing the bootstrapper implementation

We have installed Ninject now, and we have set the base requirements for our bootstrapper.

Now we need to add the base interfaces and classes we will need later in our implementation, these include:

A base view-model, that we can use to inject the required parts we need, like the repository and the unit-of-work

  • Basic service for navigation
  • Basic service to handle application settings

With service interfaces are meant here. This interfaces offer “services” like navigation and settings management. This can be confusing, because the most people think about web-services or similar things when hearing that.

Adding the services

To have everything in place, create a new project-folder inside our portable core project and add two new interfaces:

  • INavigationService
  • ISettingsService

Here is the code for the two files:

using System; 
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MVVMWindowsPhone.Core.Portable.Services
{
//
// The navigation service to
// enable page navigation.
// For all our platforms.
//
public interface INavigationService
{
//
// Navigate to a specific page.
// Used for Windows phone.
//
//The absolute uri to the page to navigate to.
void NavigateTo(Uri page);
//
// Used for Windows 8.
//
//
void NavigateTo(Type pageToNavigateTo);
//
// Go back to
// the previous page.
// Used for Windows Phone and Windows 8.
//
void GoBack();
}
}

We have an overload of the NavigateTo method here. The first one is for Windows Phone and the second one is for Windows 8. The GoBack method is for both systems. For the sake of simplicity I am not breaking this down into a partial interface or two single interfaces.

using System; 
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MVVMWindowsPhone.Core.Portable.Services
{
//
// This is our settings
// servcie. To save and
// load settgins.
//
public interface ISettingsService
{
//
// Save a setting.
//
// The type to save.
//The value of T to save.
//The key under which to save the value.
//
bool SaveSetting(T value, string key);
//
// Load a setting.
//
// The type to save.
//The value of T to save.
//The key under which to save the value.
//
T LoadSetting(T value, string key);
//
// Remove a settings entry
// with a specific key.
//
//The key to pin the settings entry to delete.
//
bool RemoveSetting(string key);
}
}

This “Service” has three members, where two of it are generic. We will later use those generic methods to serialize complete types on each platform with serialization frameworks.

The base view-model

We are using MVVM-Light here (the portable branch), this means that we want to get all the advantages of MVVM-Light we want to use. The basic view-model class in MVVM-Light is called ViewModelBase. This class offers us the following things out of the box:

  • Design-Time mode detection (in the PCL version, for different systems)
  • Messaging
  • PropertyChanged notification (inheriting from ObservableObject another MVVM-Class that offers various possibilities to raise a property changed notification)

Here is the original ViewModelBase class from the MVVM-Light (the portable version) just to give you a feeling about what it offers (Courtesy of MVVM-Light):

using GalaSoft.MvvmLight.Helpers; 
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace GalaSoft.MvvmLight
{
    public abstract class ViewModelBase : ObservableObject, ICleanup
    {
        private static bool? _isInDesignMode;
        private IMessenger _messengerInstance;
        public bool IsInDesignMode
        {
            get
            {
                return ViewModelBase.IsInDesignModeStatic;
            }
        }
        public static bool IsInDesignModeStatic
        {
            get
            {
                if (!ViewModelBase._isInDesignMode.HasValue)
                {
                    ViewModelBase._isInDesignMode = new bool?(ViewModelBase.IsInDesignModePortable());
                }
            return ViewModelBase._isInDesignMode.Value;
        }
    }
    protected IMessenger MessengerInstance
    {
        get
        {
            IMessenger messenger = this._messengerInstance;
            IMessenger @default = messenger;
            if (messenger == null)
            {
                @default = Messenger.Default;
            }
            return @default;
        }
        set
        {
            this._messengerInstance = value;
        }
    }
    public ViewModelBase() : this(null)
    {
    }
    public ViewModelBase(IMessenger messenger)
    {
        this.MessengerInstance = messenger;
    }
    protected virtual void Broadcast(T oldValue, T newValue, string propertyName)
    {
        PropertyChangedMessage propertyChangedMessage = new PropertyChangedMessage(this, oldValue, newValue, propertyName);
        this.MessengerInstance.Send&lt;propertychangedmessage&gt;(propertyChangedMessage);
    }
    public virtual void Cleanup()
    {
        this.MessengerInstance.Unregister(this);
    }
    private static bool IsInDesignModeMetro()
    {
        bool value;
        try
        {
            Type type = Type.GetType(“Windows.ApplicationModel.DesignMode, Windows, ContentType=WindowsRuntime”);
            PropertyInfo property = type.GetProperty(“DesignModeEnabled”, BindingFlags.Static | BindingFlags.Public);
            value = (bool)property.GetValue(null, null);
        }
        catch
        {
            value = false;
        }
        return value;
    }
    private static bool IsInDesignModeNet()
    {
        bool flag;
        try
        {
            Type type = Type.GetType(“System.ComponentModel.DesignerProperties, " + 
              "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″);
            object value = type.GetField(“IsInDesignModeProperty”, 
              BindingFlags.Static | BindingFlags.Public).GetValue(null);
            Type type1 = Type.GetType(“System.ComponentModel.DependencyPropertyDescriptor," + 
              " WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″);
            Type type2 = Type.GetType(“System.Windows.FrameworkElement, " + 
              "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″);
            MethodInfo[] methods = type1.GetMethods(BindingFlags.Static | BindingFlags.Public);
            MethodInfo[] methodInfoArray = methods;
            MethodInfo methodInfo = ((IEnumerable)methodInfoArray).Single((MethodInfo mi) =&gt; {
                if (mi.Name != “FromProperty”)
                {
                    return false;
                }
                else
                {
                    return (int)mi.GetParameters().Length == 2;
                }
            });
            object[] objArray = new object[] { value, type2 };
            object obj = methodInfo.Invoke(null, objArray);
            PropertyInfo property = type1.GetProperty(“Metadata”, BindingFlags.Instance | BindingFlags.Public);
            object value1 = property.GetValue(obj, null);
            Type type3 = Type.GetType(“System.Windows.PropertyMetadata, " + 
              "WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″);
            PropertyInfo propertyInfo = type3.GetProperty(“DefaultValue”, 
               BindingFlags.Instance | BindingFlags.Public);
            bool flag1 = (bool)propertyInfo.GetValue(value1, null);
            flag = flag1;
        }
        catch
        {
            flag = false;
        }
        return flag;
    }
    private static bool IsInDesignModePortable()
    {
        DesignerPlatformLibrary detectedDesignerLibrary = DesignerLibrary.DetectedDesignerLibrary;
        if (detectedDesignerLibrary != DesignerPlatformLibrary.WinRT)
        {
            if (detectedDesignerLibrary != DesignerPlatformLibrary.Silverlight)
            {
                if (detectedDesignerLibrary != DesignerPlatformLibrary.Net)
                {
                    return false;
                }
                else
                {
                    return ViewModelBase.IsInDesignModeNet();
                }
            }
            else
            {
                bool flag = ViewModelBase.IsInDesignModeSilverlight();
                if (!flag)
                {
                    flag = ViewModelBase.IsInDesignModeNet();
                }
                return flag;
            }
        }
        else
        {
            return ViewModelBase.IsInDesignModeMetro();
        }
    }
    private static bool IsInDesignModeSilverlight()
    {
        bool value;
        try
        {
            Type type = Type.GetType(“System.ComponentModel.DesignerProperties," + 
              " System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e”);
            PropertyInfo property = type.GetProperty(“IsInDesignTool”, BindingFlags.Static | BindingFlags.Public);
            value = (bool)property.GetValue(null, null);
        }
        catch
        {
            value = false;
        }
        return value;
    }
    protected virtual void RaisePropertyChanged(string propertyName, T oldValue, T newValue, bool broadcast)
    {
        if (!string.IsNullOrEmpty(propertyName))
        {
            this.RaisePropertyChanged(propertyName);
            if (broadcast)
            {
                this.Broadcast(oldValue, newValue, propertyName);
            }
            return;
        }
        else
        {
            throw new ArgumentException(“This method cannot be called with an empty string”, “propertyName”);
        }
    }
    protected virtual void RaisePropertyChanged(Expression&lt;func&gt; 
              propertyExpression, T oldValue, T newValue, bool broadcast)
    {
        PropertyChangedEventHandler propertyChangedHandler = base.PropertyChangedHandler;
        if (propertyChangedHandler != null || broadcast)
        {
            string propertyName = base.GetPropertyName(propertyExpression);
            if (propertyChangedHandler != null)
            {
                propertyChangedHandler(this, new PropertyChangedEventArgs(propertyName));
            }
            if (broadcast)
            {
                this.Broadcast(oldValue, newValue, propertyName);
            }
        }
    }
    protected bool Set(Expression&lt;func&gt; propertyExpression, ref T field, T newValue, bool broadcast)
    {
        if (!EqualityComparer.Default.Equals(field, newValue))
        {
            this.RaisePropertyChanging(propertyExpression);
            T t = field;
            field = newValue;
            this.RaisePropertyChanged(propertyExpression, t, field, broadcast);
            return true;
        }
        else
        {
            return false;
        }
    }
    protected bool Set(string propertyName, ref T field, T newValue, bool broadcast)
    {
        if (!EqualityComparer.Default.Equals(field, newValue))
        {
            this.RaisePropertyChanging(propertyName);
            T t = field;
            field = newValue;
            this.RaisePropertyChanged(propertyName, t, field, broadcast);
            return true;
        }
        else
        {
            return false;
            }
        }
    }
}

Add a new class to the folder ViewModel (in our portable core project) we added it in the last part before and name it BaseViewModel.cs. We will inherit from ViewModelBase and add some additional features we need for our sample:

using System; 
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;
using MVVMWindowsPhone.Core.Portable.DAL;
using System.Collections.ObjectModel;
using MVVMWindowsPhone.Core.Portable.Services;
using Ninject;
namespace MVVMWindowsPhone.Core.Portable.ViewModel
{
//
// Our base-view model
// based on the ViewModelBase
// of MVVM-Light portable, with
// two generic type parameters
// to support our IRepository.
//
//
//
public class BaseViewModel&lt;t,u&gt;:ViewModelBase where U:class where T:class
{
//
// The data we need for our ViewModel.
//
private ObservableCollection data;
//
// Our navigation service we need.
//
private readonly INavigationService navigationService;
//
// The data we need for our ViewModel.
//
public ObservableCollection Data
{
get { return data; }
set { data = value; }
}
//
// The repository we use.
//
private readonly IRepository&lt;t,u&gt; repository;
//
// Our constructor.
// This one will be used
// to inject our repository.
//
//
[Inject]
public BaseViewModel(IRepository&lt;t,u&gt; repo,INavigationService navService)
{
this.repository = repo;
this.navigationService = navService;
this.Model = new ObservableCollection();
}
}
}

We inherit from the ViewModelBase class and add two generic type parameters T,U. Where T stands for the type of our current model-class we want to use and U stands for the current unit-of-work we want to use. The injection-technique we use here is constructor-injection. You can see the “[Injection]” attribute placed on the constructor of our BaseViewModel.

We have the following members in our BaseViewModel:

  • Data => This is our main collection that will contain our entries, based on the type for our model we pass through the generic parameter U
  • navigationService => Here we pass an instance of the INavigationService interface. This will be done through constructor injection utilizing Ninject
  • repository => this is the IRepository implementation that we will inject into our view-model through constructor injection

The View-Model-Locator

The VML (View-Model-Locator) is responsible to deliver the view-models for our view. The current implementation of the VML is quite different from the standard VML of MVVM-Light:

using GalaSoft.MvvmLight; 
using MVVMWindowsPhone.Core.Portable.Bootstrapper;
using MVVMWindowsPhone.Core.Portable.DAL;
using MVVMWindowsPhone.Core.Portable.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
namespace MVVMWindowsPhone.Core.Portable.ViewModel
{
//
// The ViewModel locator.
//
public class ViewModelLocator:IViewModelLocator
{
//
// Our view models.
//
Dictionary&lt;string, ViewModelBase&gt; viewModels;
//
// Our view models.
//
public Dictionary&lt;string, ViewModelBase&gt; ViewModels
{
get { return viewModels; }
set { viewModels = value; }
}
//
// Standard constructor.
//
public ViewModelLocator()
{
ViewModels = new Dictionary&lt;string,ViewModelBase&gt;();
}
//
// Set and get your ViewModels
// here.
//
//The name of the viewmodel to get or set.
// The viewmodel selected.
public dynamic this[string viewModelName]
{
get
{
if(ViewModels.ContainsKey(viewModelName))
{
return this.ViewModels[viewModelName];
}
else
{
return null;
}
}
}
}
}

Because of the generic implementation of the BaseViewModel class, I came up with the indexer of type dynamic. You can see that there is a Dictionary<string,viewmodelbase> defined. Our BaseViewModel class has two generic type parameters, that can be reference types of any kind. Therefore creating a VML with those type parameters would allow us only to host view-models of a certain type, and that’s not what is the goal here. Therefore the dictionary ViewModels contains the base type ViewModelBase. The indexer is of type dynamic. Therefore getting a property from our ViewModel will be resolved dynamically at runtime.

And here is the corresponding interface IViewModelLocator used later for injection:

using System; 
using System.Collections.Generic;
using GalaSoft.MvvmLight;
namespace MVVMWindowsPhone.Core.Portable.Bootstrapping
{
//
// The base for our Viewmodel locator.
//
interface IViewModelLocator
{
//
// The indexer to get the
// right ViewModel.
//
//
//
dynamic this[string viewModelName] { get; }
//
// The dictionary to
// save the ViewModels to.
//
Dictionary&lt;string, ViewModelBase&gt; ViewModels { get; set; }
}
}

The bootstrapper

Through the basic structure of PCL projects, supporting different platforms, we move to the direction of abstracting as much as possible of our portable core by using interfaces and abstract classes.

The next thing to do, is to install the respective components for Ninject in our Windows 8 and Windows Phone 8 projects. Fire up the NuGet reference dialog and install the portable version of Ninject in both projects. This will add some platform-specific assemblies to each of the platforms. Check the reference folder after installing the assemblies.

NinjectReferencesWin8WP8

This should give you (and hopefully me, too, once and forever) the hint, how all the PCL stuff works:

  • Add a base component like the Ninject portable that can be understood by the chosen platforms configured for the PCL library
  • Add the abstractions like interfaces, abstract classes to the PCL library, to be used for every single platform
  • Add the specific platform dependencies to your chosen platforms like Win8, WP8, what ever you chose
  • Add an entry point (a method) to new-up the needed classes to make that stuff run

The current implementation for the basic bootstapper, requires to add two new files to our PCL project:

  • IBootStrapper.cs => The base interface for our bootstrapper
  • SimpleBootstrapper.cs (an abstract class to be implemented later in WP8 and Win8)

As always, you can name the files to whatever you prefer.

This interface has the following members:

  • Container of type IKernel => the Ninject kernel we will use later
  • ViewModelLocator of type IViewModelLocator => We will use this one later to “load” our view-models via Ninject
  • Modules of type IList => our Ninject modules will be “loaded” into this member

And here is the abstract class we use later to derive from in our WP8 and Win8 projects:

using MVVMWindowsPhone.Core.Portable.Bootstrapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject;
using Ninject.Modules;
namespace MVVMWindowsPhone.Core.Portable.Bootstrapping
{
    //
    // This is a simple implementation of our
    // bootstrapper for Ninject. This needs to
    // be
    //
    public abstract class SimpleBootstrapper:IBootstrapper
    {
        //
        // The Ninject-Kernel,
        // our Container in terms
        // of
        //
        private IKernel container;
        //
        // The list of modules to import.
        //
        private IList modules;
        //
        //
        //
        private IViewModelLocator viewModelLocator;
        //
        // The container (Ninject Kernel)
        // used to bind the types to
        // the interfaces.
        //
        public IKernel Container
        {
            get
            {
                return this.container;
            }
            set
            {
                this.container = value;
            }
        }
        //
        // The ninject modules
        // to be loaded by the
        // container (Ninject Kernel)
        //
        public IList Modules
        {
            get
            {
                return this.modules;
            }
            set
            {
                this.modules = value;
            }
        }
        //
        // The ViewModel-Locator
        // that holds the instantiated
        // ViewModels to bind the
        // XAML against.
        //
        public IViewModelLocator ViewModelLocator
        {
            get
            {
                return this.viewModelLocator;
            }
            set
            {
                this.viewModelLocator = value;
            }
        }
        //
        // The standard constructor.
        //
        public SimpleBootstrapper()
        {
           //Nothing here curretnly.
        }
        //
        // This method is defined
        // as virtual, to enable
        // an entry point for Ninject
        // like stated by Owen on Twitter.
        //
        public virtual void ConfigureBootstrapper()
        {
           //Not implemented yet.
        }
    }
}

We will use the ConfigureBootstrapper method later, to configure and setup everything we need to work successfully with Ninject.

In the next part, I show you how to put all this stuff together.

Thank you for reading. If you have ideas, or want to see something additional here, let me know. All the code is hosted on GitHub: https://github.com/Injac/WindowsPhone8MVVM

The post MVVM for Windows Phone Developers–Use it Guys! (Part 3) appeared first on @awsomedevsigner.

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