Click here to Skip to main content
15,886,518 members
Articles / Programming Languages / C#

MvvmCross - v3 - Writing a First App

Rate me:
Please Sign up or sign in to vote.
4.91/5 (8 votes)
23 Mar 2013Ms-PL2 min read 74.1K   34  
MvvmCross - A CrossPlatform MVVM Sample
<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&#39;s Go Portable</h2>
<p>
    MvvmCross application&#39;s are normally structured with:</p>
<ul>
    <li>
        one shared &#39;core&#39; 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&#39;s exactly what we&#39;ll do here.</p>
<p>
    To create the core, you can use the Visual Studio project template wizards, but here we&#39;ll instead build up a new project &#39;from empty&#39;.</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>
    &nbsp;</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 &#39;Add references&#39; 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">&nbsp;</font></p>
<h2>
    Add the Tip Calculation service</h2>
<p>
    Create a folder called &#39;Services&#39;</p>
<p>
    Within this folder create a new Interface which will be used for calculating tips:</p>
<p>
    &nbsp;</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>
    &nbsp;</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&#39;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 &#39;model&#39; for the user interface - which is, of course, a &#39;ViewModel&#39;</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>
    &nbsp;</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(() =&gt; SubTotal); Recalcuate(); }
        }

        private int _generosity;

        public int Generosity
        {
            get { return _generosity; }
            set { _generosity = value; RaisePropertyChanged(() =&gt; Generosity); Recalcuate(); }
        }

        private double _tip;

        public double Tip
        {
            get { return _tip; }
            set { _tip = value; RaisePropertyChanged(() =&gt; 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 &#39;Create the Application&#39;. 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(() =&gt; SubTotal); Recalculate(); }
}

private int _generosity;
public int Generosity
{
    get { return _generosity; }
    set {  _generosity = value; RaisePropertyChanged(() =&gt; Generosity); Recalculate(); }
}

private double _tip;
public double Tip
{
    get { return _tip; }
    private set {  _tip = value; RaisePropertyChanged(() =&gt; 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>
    &#39;Registration&#39; here means creating an &#39;Inversion of Control&#39; - 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&lt;ICalculation, Calculation&gt;();
</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&lt;TipViewModel&gt;();
     Mvx.RegisterSingleton&lt;IMvxAppStart&gt;(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&#39;s what App.cs looks like:</p>
<p>
    &nbsp;</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&lt;ICalculation,Calculation&gt;();
            Mvx.RegisterSingleton&lt;IMvxAppStart&gt;(new MvxAppStart&lt;TipViewModel&gt;());
        }
    }
}
</font></code></pre>
<h2>
    Note: What is &#39;Inversion of Control&#39;?</h2>
<p>
    We won&#39;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 &#39;constructor injection&#39;:</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&#39;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&#39;ll start looking at how to add a first UI to this MvvmCross application.</p>
<h2>
    History</h2>
<p>
    22nd March 2013 - First&nbsp;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.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Software Developer Cirrious Ltd
United Kingdom United Kingdom
Developing software since 1982. Currently engrossed in both cloud and mobile technologies. Currently spending far too much time on MvvmCross... and loving every second of it Smile | :)

Comments and Discussions