|
<h1>
MvvmCross TipCalc - Step1 Creating the Core Portable Application</h1>
<h2>
Introduction</h2>
<p>
This article is step 1 in the TipCalc tutorial for MvvmCross v3 - Hot Tuna!</p>
<h2>
Let's Go Portable</h2>
<p>
MvvmCross application's are normally structured with:</p>
<ul>
<li>
one shared 'core' Portable Class Library (PCL) project
<ul>
<li>
containing as much code as possible: models, view models, services, converters, etc</li>
</ul>
</li>
<li>
one UI project per platform
<ul>
<li>
each containing the bootstrap and view-specific code for that platform</li>
</ul>
</li>
</ul>
<p>
Normally, you start development from the core project - and that's exactly what we'll do here.</p>
<p>
To create the core, you can use the Visual Studio project template wizards, but here we'll instead build up a new project 'from empty'.</p>
<h2>
Create a Portable Library</h2>
<p>
Using Visual Studio, create your new PCL using the File|New Project wizard.</p>
<p>
Call it something like TipCalc.Core.csproj</p>
<p>
When asked to choose platforms, select all of WindowsPhone, WindowsStore (.Net4.5), Xamarin.Android and Xamarin.iOS - this will ensure that the PCL is in <strong>Profile104</strong>. This profile defines a small subset of .Net that contains parts of the assemblies for:</p>
<ul>
<li>
mscorlib</li>
<li>
System.Core</li>
<li>
System.Net</li>
<li>
System.Runtime.Serialization</li>
<li>
System.ServiceModel</li>
<li>
System.Windows</li>
<li>
System.Xml</li>
<li>
System.Xml.Linq</li>
<li>
System.Xml.Serialization</li>
</ul>
<p>
Importantly for us this Profile104 includes everything we need to build our Mvvm applications</p>
<h2>
</h2>
<h2>
Delete Class1.cs</h2>
<p>
No-one really needs a <font color="#000000"><code>Class1</code> :)</font></p>
<h2>
<font color="#000000">Add references to the CrossCore and MvvmCross assemblies</font></h2>
<p>
<font color="#000000">Use 'Add references' to add links to the 2 Portable Class Libraries.</font></p>
<ul>
<li>
<font color="#000000"><strong>Cirrious.CrossCore.dll</strong> </font>
<ul>
<li>
<font color="#000000">core interfaces and concepts including Trace, IoC and Plugin management</font></li>
</ul>
</li>
<li>
<font color="#000000"><strong>Cirrious.MvvmCross.dll</strong> </font>
<ul>
<li>
<font color="#000000">Mvvm classes - including base classes for your MvxApplication and your MvxViewModels</font></li>
</ul>
</li>
</ul>
<p>
<font color="#000000">Normally these will be found in a folder path like <em>{SolutionRoot}/Libs/Mvx/Portable/</em> </font></p>
<p>
<font color="#000000"> </font></p>
<h2>
Add the Tip Calculation service</h2>
<p>
Create a folder called 'Services'</p>
<p>
Within this folder create a new Interface which will be used for calculating tips:</p>
<p>
</p>
<pre>
<code><font color="#000000">public interface ICalculation
{
double TipAmount(double subTotal, int generosity);
}
</font></code></pre>
<p>
Within this folder create an implementation of this interface:</p>
<p>
</p>
<pre>
<code><font color="#000000">public class Calculation : ICalculation
{
public double TipAmount(double subTotal, int generosity)
{
return subTotal * ((double)generosity))/100.0;
}
}
</font></code></pre>
<p>
This provides us with some simple business logic for our app</p>
<h2>
Add the ViewModel</h2>
<p>
At a sketch level, we want a user interface that:</p>
<ul>
<li>
uses:
<ul>
<li>
our calculation service to calculate the tip</li>
</ul>
</li>
<li>
has inputs of:
<ul>
<li>
the current bill (the subTotal)</li>
<li>
a feeling for how much tip we'd like to leave (the generosity)</li>
</ul>
</li>
<li>
has output displays of:
<ul>
<li>
the calculated tip to leave</li>
</ul>
</li>
</ul>
<p>
To represent this user interface we need to build a 'model' for the user interface - which is, of course, a 'ViewModel'</p>
<p>
Within MvvmCross, all ViewModels should inherit from <font color="#000000"><code>MvxViewModel</code>.</font></p>
<p>
So now create a ViewModels folder in our project, and in this folder add a new <font color="#000000"><code>TipViewModel</code> class like:</font></p>
<p>
</p>
<pre>
<code><font color="#000000">using Cirrious.MvvmCross.ViewModels;
namespace TipCalc.Core
{
public class TipViewModel : MvxViewModel
{
private readonly ICalculation _calculation;
public TipViewModel(ICalculation calculation)
{
_calculation = calculation;
}
public override void Start()
{
_subTotal = 100;
_generosity = 10;
Recalcuate();
base.Start();
}
private double _subTotal;
public double SubTotal
{
get { return _subTotal; }
set { _subTotal = value; RaisePropertyChanged(() => SubTotal); Recalcuate(); }
}
private int _generosity;
public int Generosity
{
get { return _generosity; }
set { _generosity = value; RaisePropertyChanged(() => Generosity); Recalcuate(); }
}
private double _tip;
public double Tip
{
get { return _tip; }
set { _tip = value; RaisePropertyChanged(() => Tip);}
}
private void Recalcuate()
{
Tip = _calculation.TipAmount(SubTotal, Generosity);
}
}
}
</font></code></pre>
<p>
For many of you, this <font color="#000000"><code>TipViewModel</code> will already make sense to you. If it does then <strong>skip ahead</strong> to 'Create the Application'. If not, then here are some simple explanations:</font></p>
<ul>
<li>
<p>
the <font color="#000000"><code>TipViewModel</code> is constructed with an <code>ICalculation</code> service</font></p>
<pre>
<code><font color="#000000">private readonly ICalculation _calculation;
public TipViewModel(ICalculation calculation)
{
_calculation = calculation;
}
</font></code></pre>
</li>
<li>
<p>
after construction, the <font color="#000000"><code>TipViewModel</code> will be started - during this it sets some initial values.</font></p>
<pre>
<code><font color="#000000">public override void Start()
{
// set some start values
SubTotal = 100.0;
Generosity = 10;
Recalculate();
}
</font></code></pre>
</li>
<li>
<p>
the view data held within the <font color="#000000"><code>TipViewModel</code> is exposed through properties. </font></p>
<ul>
<li>
Each of these properties is backed by a private member variable</li>
<li>
Each of these properties has a get and a set</li>
<li>
The set accessor for <font color="#000000"><code>Tip</code> is marked private</font></li>
<li>
All of the set accessors call <font color="#000000"><code>RaisePropertyChanged</code> to tell the base <code>MvxViewModel</code> that the data has changed</font></li>
<li>
<p>
The <font color="#000000"><code>SubTotal</code> and <code>Generosity</code> set accessors also call <code>Recalculate()</code></font></p>
<pre>
<code><font color="#000000">private double _subTotal;
public double SubTotal
{
get { return _subTotal; }
set { _subTotal = value; RaisePropertyChanged(() => SubTotal); Recalculate(); }
}
private int _generosity;
public int Generosity
{
get { return _generosity; }
set { _generosity = value; RaisePropertyChanged(() => Generosity); Recalculate(); }
}
private double _tip;
public double Tip
{
get { return _tip; }
private set { _tip = value; RaisePropertyChanged(() => Tip); }
}
</font></code></pre>
</li>
</ul>
</li>
<li>
<p>
The <font color="#000000"><code>Recalculate</code> method uses the <code>_calculation</code> service to update <code>Tip</code> from the current values in <code>SubTotal</code> and <code>Generosity</code></font></p>
<pre>
<code><font color="#000000">private void Recalculate()
{
Tip = _calculation.TipAmount(SubTotal, Generosity);
}
</font></code></pre>
</li>
</ul>
<h2>
Add the App(lication)</h2>
<p>
With our <font color="#000000"><code>Calculation</code> service and <code>TipViewModel</code> defined, we now just need to add the main <code>App</code> code.</font></p>
<p>
This code:</p>
<ul>
<li>
will sit in a single class within the root folder of our PCL core project.</li>
<li>
this class will inherits from the <font color="#000000"><code>MvxApplication</code> class</font></li>
<li>
this class is normally just called <font color="#000000"><code>App</code> </font></li>
<li>
this class is responsible for providing:
<ul>
<li>
registration of which interfaces and implementations the app uses</li>
<li>
registration of which <font color="#000000"><code>ViewModel</code> the <code>App</code> will show when it starts</font></li>
<li>
control of how <font color="#000000"><code>ViewModel</code>s are located - although most applications normally just use the default implementation of this supplied by the base <code>MvxApplication</code> class.</font></li>
</ul>
</li>
</ul>
<p>
'Registration' here means creating an 'Inversion of Control' - IoC - record for an interface. This IoC record tells the MvvmCross framework what to do when anything asks for an instance of that interface.</p>
<p>
For our Tip Calculation app:</p>
<ul>
<li>
<p>
we register the <font color="#000000"><code>Calculation</code> class to implement the <code>ICalculation</code> service</font></p>
<pre>
<code><font color="#000000"> Mvx.RegisterType<ICalculation, Calculation>();
</font></code></pre>
<p>
this line tells the MvvmCross framework that whenever any code requests an <font color="#000000"><code>ICalculation</code> reference, then the framework should create a new instance of <code>Calculation</code></font></p>
</li>
<li>
<p>
we want the app to start with the <code><font color="#000000">TipViewModel</font></code></p>
<pre>
<code><font color="#000000"> var appStart = new MvxAppStart<TipViewModel>();
Mvx.RegisterSingleton<IMvxAppStart>(appStart);
</font></code></pre>
<p>
this line tells the MvvmCross framework that whenever any code requests an <font color="#000000"><code>IMvxAppStart</code> reference, then the framework should return that same <code>appStart</code> instance.</font></p>
</li>
</ul>
<p>
So here's what App.cs looks like:</p>
<p>
</p>
<pre>
<code><font color="#000000">using Cirrious.CrossCore.IoC;
using Cirrious.MvvmCross.ViewModels;
namespace TipCalc.Core
{
public class App : MvxApplication
{
public App()
{
Mvx.RegisterType<ICalculation,Calculation>();
Mvx.RegisterSingleton<IMvxAppStart>(new MvxAppStart<TipViewModel>());
}
}
}
</font></code></pre>
<h2>
Note: What is 'Inversion of Control'?</h2>
<p>
We won't go into depth here about what IoC - Inversion of Control - is.</p>
<p>
Instead, we will just say that:</p>
<ul>
<li>
within each MvvmCross application, there is a single special object - a <font color="#000000"><code>singleton</code> </font></li>
<li>
This <font color="#000000"><code>singleton</code> lives within the <code>Mvx</code> static class.</font></li>
<li>
The application startup code can use the <font color="#000000"><code>Mvx.Register</code> methods in order to specify what will implement <code>interface</code>s during the lifetime of the app.</font></li>
<li>
After this has been done, then later in the life when any code needs an <font color="#000000"><code>interface</code> implementation, then it can request one using the <code>Mvx.Resolve</code> methods.</font></li>
</ul>
<p>
One common pattern that is seen is 'constructor injection':</p>
<ul>
<li>
Our <font color="#000000"><code>TipViewModel</code> uses this pattern. </font></li>
<li>
It presents a constructor like: <font color="#000000"><code>public TipViewModel(ICalculation calculation)</code>. </font></li>
<li>
When the app is running a part of the MvvmCross framework called the <font color="#000000"><code>ViewModelLocator</code> is used to find and create <code>ViewModel</code>s</font></li>
<li>
when a <font color="#000000"><code>TipViewModel</code> is needed, the <code>ViewModelLocator</code> uses a call to <code>Mvx.IocConstruct</code> to create one.</font></li>
<li>
This <font color="#000000"><code>Mvx.IocConstruct</code> call creates the <code>TipViewModel</code> using the <code>ICalculation</code> implementation that it finds using <code>Mvx.Resolve</code> </font></li>
</ul>
<p>
This is obviously only a very brief introduction.</p>
<p>
If you would like to know more, please see look up some of the excellent tutorials out there on the Internet - like <a href="http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/"><u><font color="#0066cc">http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/</font></u></a></p>
<h2>
The Core project is complete :)</h2>
<p>
Just to recap the steps we've followed:</p>
<ol>
<li>
<p>
We created a new PCL project using Profile104</p>
</li>
<li>
<p>
We added references to two PCL libraries - CrossCore and MvvmCross</p>
</li>
<li>
<p>
We added a <font color="#000000"><code>ICalculation</code> interface and implementation pair</font></p>
</li>
<li>
<p>
We added a <font color="#000000"><code>TipViewModel</code> which:</font></p>
<ul>
<li>
inherited from <font color="#000000"><code>MvxViewModel</code> </font></li>
<li>
used <font color="#000000"><code>ICalculation</code> </font></li>
<li>
presented a number of public properties each of which called <font color="#000000"><code>RaisePropertyChanged</code> </font></li>
</ul>
</li>
<li>
<p>
We added an <font color="#000000"><code>App</code> which:</font></p>
<ul>
<li>
inherited from <font color="#000000"><code>MvxApplication</code> </font></li>
<li>
registered the <font color="#000000"><code>ICalculation</code>/<code>Calculation</code> pair</font></li>
<li>
registered a special start object for <font color="#000000"><code>IMvxAppStart</code> </font></li>
</ul>
</li>
</ol>
<p>
These are the same steps that you need to go through for every new MvvmCross application.</p>
<h2>
Moving on</h2>
<p>
Next we'll start looking at how to add a first UI to this MvvmCross application.</p>
<h2>
History</h2>
<p>
22nd March 2013 - First Submission</p>
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.