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
{
public interface IUnitOfWork where T:class
{
T Context {get; set;}
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
{
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 objectIQueyable 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.
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
{
public interface INavigationService
{
void NavigateTo(Uri page);
void NavigateTo(Type pageToNavigateTo);
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
{
public interface ISettingsService
{
bool SaveSetting(T value, string key);
T LoadSetting(T value, string key);
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<propertychangedmessage>(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) => {
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<func>
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<func> 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
{
public class BaseViewModel<t,u>:ViewModelBase where U:class where T:class
{
private ObservableCollection data;
private readonly INavigationService navigationService;
public ObservableCollection Data
{
get { return data; }
set { data = value; }
}
private readonly IRepository<t,u> repository;
[Inject]
public BaseViewModel(IRepository<t,u> 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
{
public class ViewModelLocator:IViewModelLocator
{
Dictionary<string, ViewModelBase> viewModels;
public Dictionary<string, ViewModelBase> ViewModels
{
get { return viewModels; }
set { viewModels = value; }
}
public ViewModelLocator()
{
ViewModels = new Dictionary<string,ViewModelBase>();
}
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
{
interface IViewModelLocator
{
dynamic this[string viewModelName] { get; }
Dictionary<string, ViewModelBase> 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.
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
{
public abstract class SimpleBootstrapper:IBootstrapper
{
private IKernel container;
private IList modules;
private IViewModelLocator viewModelLocator;
public IKernel Container
{
get
{
return this.container;
}
set
{
this.container = value;
}
}
public IList Modules
{
get
{
return this.modules;
}
set
{
this.modules = value;
}
}
public IViewModelLocator ViewModelLocator
{
get
{
return this.viewModelLocator;
}
set
{
this.viewModelLocator = value;
}
}
public SimpleBootstrapper()
{
}
public virtual void ConfigureBootstrapper()
{
}
}
}
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.