Click here to Skip to main content
15,895,799 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.2K   34  
MvvmCross - A CrossPlatform MVVM Sample
<h1>
    MvvmCross TipCalc - Step4 Creating a Windows Phone UI</h1>
<h2>
    Introduction</h2>
<p>
    This article is step&nbsp;4 in the TipCalc tutorial for MvvmCross v3 - Hot Tuna!</p>
<h2>
    The story so far...</h2>
<div id="wiki-content">
    <div class="wrap">
        <div class="gollum-markdown-content instapaper_body" id="wiki-body">
            <div class="markdown-body">
                <div id="wiki-content">
                    <div class="wrap">
                        <div class="gollum-markdown-content instapaper_body" id="wiki-body">
                            <div class="markdown-body">
                                <div id="wiki-content">
                                    <div class="wrap">
                                        <div class="gollum-markdown-content instapaper_body" id="wiki-body">
                                            <div class="markdown-body">
                                                <p>
                                                    We started with the goal of creating an app to help calculate what tip to leave in a restaurant</p>
                                                <p>
                                                    We had a plan to produce a UI based on this concept:</p>
                                                <p>
                                                    <img alt="Sketch" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_Sketch.png"></p>
                                                <p>
                                                    To satisfy this we built a &#39;Core&#39; Portable Class Library project which contained:</p>
                                                <ul>
                                                    <li>
                                                        our &#39;business logic&#39; - <font color="#000000"><code>ICalculation</code> </font></li>
                                                    <li>
                                                        our ViewModel - <font color="#000000"><code>TipViewModel</code> </font></li>
                                                    <li>
                                                        our <font color="#000000"><code>App</code> which contains the application wiring, including the start instructions.</font></li>
                                                </ul>
                                                <p>
                                                    We then added our first User Interface - for Xamarin.Android:</p>
                                                <p>
                                                    <img alt="Android" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_Android_Styled.png"></p>
                                                <p>
                                                    We then added our second User Interface - for Xamarin.iOS:</p>
                                                <p>
                                                    <img alt="v1" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_Touch_Sim.png"></p>
                                                <p>
                                                    For our next project, let&#39;s shift to WindowsPhone.</p>
                                                <p>
                                                    To create an WindowsPhone MvvmCross UI, you can use the Visual Studio project template wizards, but here we&#39;ll instead build up a new project &#39;from empty&#39;, just as we did for the Core, Android and iOS projects.</p>
                                                <p>
                                                    Obviously, to work with WindowsPhone, we will need to switch back to working on the PC with Visual Studio</p>
                                                <h2>
                                                    Create a new WindowsPhone Project</h2>
                                                <p>
                                                    Add a new project to your solution - a &#39;Windows Phone App&#39; application with name <code><font color="#000000">TipCalc.UI.WP</font></code></p>
                                                <p>
                                                    For target operating system, you can choose 7.1 or 8.0 - your choice.</p>
                                                <p>
                                                    Within this, you&#39;ll find the normal WP application constructs:</p>
                                                <ul>
                                                    <li>
                                                        the App.Xaml &#39;application&#39; object</li>
                                                    <li>
                                                        the &#39;Properties&#39; folder with its AppManifest.xml and WMAppManifest.xml &#39;configuration&#39; files</li>
                                                    <li>
                                                        the MainPage.Xaml and MainPage.Xaml.cs files that define the default Page for this app</li>
                                                    <li>
                                                        some icons</li>
                                                </ul>
                                                <h2>
                                                    Delete MainPage.xaml</h2>
                                                <p>
                                                    No-one really needs a <font color="#000000"><code>MainPage</code> :)</font></p>
                                                <h2>
                                                    Add references</h2>
                                                <h3>
                                                    Add references to CoreCross and MvvmCross - PCL versions</h3>
                                                <p>
                                                    Add references to the new project for the portable libraries:</p>
                                                <ul>
                                                    <li>
                                                        <strong>Cirrious.CrossCore.dll</strong>
                                                        <ul>
                                                            <li>
                                                                core interfaces and concepts including Trace, IoC and Plugin management</li>
                                                        </ul>
                                                    </li>
                                                    <li>
                                                        <strong>Cirrious.MvvmCross.dll</strong>
                                                        <ul>
                                                            <li>
                                                                Mvvm classes - including base classes for your views and viewmodels</li>
                                                        </ul>
                                                    </li>
                                                    <li>
                                                        <strong>Cirrious.MvvmCross.Plugins.Json.dll</strong>
                                                        <ul>
                                                            <li>
                                                                Adds a PCL Newtonsoft.JSON.Net implementation - our WindowsPhone UI application will use this to navigate between Pages</li>
                                                        </ul>
                                                    </li>
                                                </ul>
                                                <p>
                                                    Normally these will be found in a folder path like <em>{SolutionRoot}/Libs/Mvx/Portable/</em></p>
                                                <h3>
                                                    Add references to CoreCross and MvvmCross - WindowsPhone specific versions</h3>
                                                <p>
                                                    Add references to the new project for the WindowsPhone specific libraries:</p>
                                                <ul>
                                                    <li>
                                                        <strong>Cirrious.CrossCore.WindowsPhone.dll</strong></li>
                                                    <li>
                                                        <strong>Cirrious.MvvmCross.WindowsPhone.dll</strong></li>
                                                </ul>
                                                <p>
                                                    Each of these extends the functionality of its PCL counterpart with WP specific additions.</p>
                                                <p>
                                                    Normally these will be found in a folder path like <em>{SolutionRoot}/Libs/Mvx/WindowsPhone/</em></p>
                                                <p>
                                                    Also, within that same folder you need to add:</p>
                                                <h3>
                                                    Add a reference to TipCalc.Core.csproj</h3>
                                                <p>
                                                    Add a reference to your <font color="#000000"><code>TipCalc.Core</code> project - the project we created in the last step which included:</font></p>
                                                <ul>
                                                    <li>
                                                        your <font color="#000000"><code>Calculation</code> service, </font></li>
                                                    <li>
                                                        your <font color="#000000"><code>TipViewModel</code> </font></li>
                                                    <li>
                                                        your <font color="#000000"><code>App</code> wiring.</font></li>
                                                </ul>
                                                <h2>
                                                    Add a Setup class</h2>
                                                <p>
                                                    Just as we said during the Android and iOS construction <em>Every MvvmCross UI project requires a <font color="#000000"><code>Setup</code> class</font></em></p>
                                                <p>
                                                    This class sits in the root namespace (folder) of our UI project and performs the initialisation of the MvvmCross framework and your application, including:</p>
                                                <ul>
                                                    <li>
                                                        the Inversion of Control (IoC) system</li>
                                                    <li>
                                                        the MvvmCross data-binding</li>
                                                    <li>
                                                        your <font color="#000000"><code>App</code> and its collection of <code>ViewModel</code>s</font></li>
                                                    <li>
                                                        your UI project and its collection of <font color="#000000"><code>View</code>s</font></li>
                                                </ul>
                                                <p>
                                                    Most of this functionality is provided for you automatically. Within your WindowsPhone UI project all you have to supply are:</p>
                                                <ul>
                                                    <li>
                                                        your <font color="#000000"><code>App</code> - your link to the business logic and <code>ViewModel</code> content</font></li>
                                                    <li>
                                                        some initialisation
                                                        <ul>
                                                            <li>
                                                                for the Json.Net plugin</li>
                                                            <li>
                                                                for the navigation mechanism</li>
                                                        </ul>
                                                    </li>
                                                </ul>
                                                <p>
                                                    For <font color="#000000"><code>TipCalc</code> here&#39;s all that is needed in Setup.cs:</font></p>
                                                <pre>
<code><font color="#000000">using Cirrious.MvvmCross.ViewModels;
using Microsoft.Phone.Controls;
using Cirrious.MvvmCross.WindowsPhone.Platform;

namespace TipCalc.UI.WP
{
    public class Setup : MvxPhoneSetup
    {
        public Setup(PhoneApplicationFrame rootFrame)
            : base(rootFrame)
        {
        }

        protected override IMvxApplication CreateApp()
        {
            return new Core.App();
        }

        protected override IMvxNavigationSerializer CreateNavigationSerializer()
        {
            Cirrious.MvvmCross.Plugins.Json.PluginLoader.Instance.EnsureLoaded(true);
            return new MvxJsonNavigationSerializer();
        }
    }
}
</font></code></pre>
                                                <h2>
                                                    Modify the App.xaml.cs to use Setup</h2>
                                                <p>
                                                    Your <font color="#000000"><code>App.xaml.cs</code> provides the WindowsPhone &#39;main application&#39; object - an object which owns the User Interface and receives some callbacks from the operating system during some key events in your application&#39;s lifecycle.</font></p>
                                                <p>
                                                    To modify this <font color="#000000"><code>App.xaml.cs</code> for MvvmCross, we need to:</font></p>
                                                <ul>
                                                    <li>
                                                        <p>
                                                            modify the constructor so that it creates and starts &#39;Setup&#39;</p>
                                                        <pre>
<code><font color="#000000">    var setup = new Setup(RootFrame);
    setup.Initialize();
</font></code></pre>
                                                    </li>
                                                    <li>
                                                        <p>
                                                            add a private field - just a boolean flag which we will set after we have done one navigation</p>
                                                        <pre>
<code><font color="#000000">private bool _hasDoneFirstNavigation = false;
</font></code></pre>
                                                    </li>
                                                    <li>
                                                        <p>
                                                            modify the Application_Launching callback so that we can intercept the first navigation, can cancel it and can delegate the initial navigation to <font color="#000000"><code>IMvxAppStart</code> instead.</font></p>
                                                        <pre>
<code><font color="#000000">// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
    RootFrame.Navigating += (navigatingSender, navigatingArgs) =&gt;
    {
        if (_hasDoneFirstNavigation)
            return;

        navigatingArgs.Cancel = true;
        _hasDoneFirstNavigation = true;
        var appStart = this.GetService&lt;IMvxAppStart&gt;();
        RootFrame.Dispatcher.BeginInvoke(appStart.Start);
    };
}
</font></code></pre>
                                                    </li>
                                                </ul>
                                                <p>
                                                    After you&#39;ve done this your code might look like:</p>
                                                <pre>
<code><font color="#000000">using System.Windows;
using System.Windows.Navigation;
using Cirrious.CrossCore.IoC;
using Cirrious.MvvmCross.ViewModels;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace TipCalc.UI.WP
{
    public partial class App : Application
    {
        /// &lt;summary&gt;
        /// Provides easy access to the root frame of the Phone Application.
        /// &lt;/summary&gt;
        /// &lt;returns&gt;The root frame of the Phone Application.&lt;/returns&gt;
        public PhoneApplicationFrame RootFrame { get; private set; }

        /// &lt;summary&gt;
        /// Constructor for the Application object.
        /// &lt;/summary&gt;
        public App()
        {
            // Global handler for uncaught exceptions. 
            UnhandledException += Application_UnhandledException;

            // Standard Silverlight initialization
            InitializeComponent();

            // Phone-specific initialization
            InitializePhoneApplication();

            // Show graphics profiling information while debugging.
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // Display the current frame rate counters.
                Application.Current.Host.Settings.EnableFrameRateCounter = true;

                // Show the areas of the app that are being redrawn in each frame.
                //Application.Current.Host.Settings.EnableRedrawRegions = true;

                // Enable non-production analysis visualization mode, 
                // which shows areas of a page that are handed off to GPU with a colored overlay.
                //Application.Current.Host.Settings.EnableCacheVisualization = true;

                // Disable the application idle detection by setting the UserIdleDetectionMode property of the
                // application&#39;s PhoneApplicationService object to Disabled.
                // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
                // and consume battery power when the user is not using the phone.
                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            }

            var setup = new Setup(RootFrame);
            setup.Initialize();
        }

        private bool _hasDoneFirstNavigation = false;

        // Code to execute when the application is launching (eg, from Start)
        // This code will not execute when the application is reactivated
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
            RootFrame.Navigating += (navigatingSender, navigatingArgs) =&gt;
            {
                if (_hasDoneFirstNavigation)
                    return;

                navigatingArgs.Cancel = true;
                _hasDoneFirstNavigation = true;
                var appStart = Mvx.Resolve&lt;IMvxAppStart&gt;();
                RootFrame.Dispatcher.BeginInvoke(() =&gt; appStart.Start());
            };
        }

        // Code to execute when the application is activated (brought to foreground)
        // This code will not execute when the application is first launched
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
        }

        // Code to execute when the application is deactivated (sent to background)
        // This code will not execute when the application is closing
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
        }

        // Code to execute when the application is closing (eg, user hit Back)
        // This code will not execute when the application is deactivated
        private void Application_Closing(object sender, ClosingEventArgs e)
        {
        }

        // Code to execute if a navigation fails
        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // A navigation has failed; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        // Code to execute on Unhandled Exceptions
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // An unhandled exception has occurred; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        #region Phone application initialization

        // Avoid double-initialization
        private bool phoneApplicationInitialized = false;

        // Do not add any additional code to this method
        private void InitializePhoneApplication()
        {
            if (phoneApplicationInitialized)
                return;

            // Create the frame but don&#39;t set it as RootVisual yet; this allows the splash
            // screen to remain active until the application is ready to render.
            RootFrame = new PhoneApplicationFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;

            // Handle navigation failures
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;

            // Ensure we don&#39;t initialize again
            phoneApplicationInitialized = true;
        }

        // Do not add any additional code to this method
        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
        {
            // Set the root visual to allow the application to render
            if (RootVisual != RootFrame)
                RootVisual = RootFrame;

            // Remove this handler since it is no longer needed
            RootFrame.Navigated -= CompleteInitializePhoneApplication;
        }

        #endregion
    }
}
</font></code></pre>
                                                <h2>
                                                    Add your View</h2>
                                                <h3>
                                                    Create an initial Page</h3>
                                                <p>
                                                    Create a Views folder</p>
                                                <p>
                                                    It is <strong>important</strong> on WindowsPhone, that this folder is called <font color="#000000"><code>Views</code> - the MvvmCross framework looks for this name by default on WindowsPhone.</font></p>
                                                <p>
                                                    Within this folder, add a new &#39;Windows Phone Portrait Page&#39; and call it <code><font color="#000000">TipView.xaml</font></code></p>
                                                <p>
                                                    This will generate:</p>
                                                <ul>
                                                    <li>
                                                        TipView.xaml</li>
                                                    <li>
                                                        TipView.xaml.cs</li>
                                                </ul>
                                                <h3>
                                                    Turn TipView into the MvvmCross View for TipViewModel</h3>
                                                <p>
                                                    Open the TipView.cs file.</p>
                                                <p>
                                                    To change TipView from a <font color="#000000"><code>PhonePage</code> into an MvvmCross view, change it so that it inherits from <code>MvxPhonePage</code></font></p>
                                                <pre>
<code><font color="#000000">public partial class TipView : MvxPhonePage
</font></code></pre>
                                                <p>
                                                    To link <font color="#000000"><code>TipView</code> to <code>TipViewModel</code> create a <code>public new TipViewModel ViewModel</code> property - exactly as you did in Xamarin.Android and Xamarin.iOS:</font></p>
                                                <pre>
<code><font color="#000000">public new TipViewModel ViewModel
{
    get { return (TipViewModel) base.ViewModel; }
    set { base.ViewModel = value; }
}
</font></code></pre>
                                                <p>
                                                    Altogether this looks like:</p>
                                                <pre>
<code><font color="#000000">using Cirrious.MvvmCross.WindowsPhone.Views;
using TipCalc.Core.ViewModels;

namespace TipCalc.UI.WP.Views
{
    public partial class TipView : MvxPhonePage
    {
        public new TipViewModel ViewModel
        {
            get { return (TipViewModel) base.ViewModel; }
            set { base.ViewModel = value; }
        }

        public TipView()
        {
            InitializeComponent();
        }
    }
}
</font></code></pre>
                                                <h3>
                                                    Edit the XAML layout</h3>
                                                <p>
                                                    Double click on the XAML file</p>
                                                <p>
                                                    This will open the XAML editor within Visual Studio.</p>
                                                <p>
                                                    I won&#39;t go into much depth at all here about how to use the XAML or do the Windows data-binding. I&#39;m assuming most readers are already coming from at least a little XAML background.</p>
                                                <p>
                                                    To make the XAML inheritance match the <font color="#000000"><code>MvxPhonePage</code> inheritance, change the outer root node of the Xaml file from:</font></p>
                                                <pre>
<code><font color="#000000">&lt;phone:PhoneApplicationPage 
    ... &gt;
    &lt;!-- content --&gt;
&lt;/phone:PhoneApplicationPage&gt;
</font></code></pre>
                                                <p>
                                                    to:</p>
                                                <pre>
<code><font color="#000000">&lt;views:MvxPhonePage
    xmlns:views=&quot;clr-namespace:Cirrious.MvvmCross.WindowsPhone.Views;assembly=Cirrious.MvvmCross.WindowsPhone&quot;
    ... &gt;
    &lt;!-- content --&gt;
&lt;/views:MvxPhonePage&gt;
</font></code></pre>
                                                <p>
                                                    To then add the XAML user interface for our tip calculator, we wi;l edit the <font color="#000000"><code>ContentPanel</code> to include:</font></p>
                                                <ul>
                                                    <li>
                                                        a <font color="#000000"><code>StackPanel</code> container, into which we add: </font>
                                                        <ul>
                                                            <li>
                                                                some <font color="#000000"><code>TextBlock</code> static text</font></li>
                                                            <li>
                                                                a bound <font color="#000000"><code>TextBox</code> for the <code>SubTotal</code> </font></li>
                                                            <li>
                                                                a bound <font color="#000000"><code>Slider</code> for the <code>Generosity</code> </font></li>
                                                            <li>
                                                                a bound <font color="#000000"><code>TextBlock</code> for the <code>Tip</code> </font></li>
                                                        </ul>
                                                    </li>
                                                </ul>
                                                <p>
                                                    This will produce XAML like:</p>
                                                <pre>
<code><font color="#000000">    &lt;Grid x:Name=&quot;ContentPanel&quot; Grid.Row=&quot;1&quot; Margin=&quot;12,0,12,0&quot;&gt;
        &lt;StackPanel&gt;
            &lt;TextBlock
                Text=&quot;SubTotal&quot;
                Style=&quot;{StaticResource PhoneTextSubtleStyle}&quot;
                /&gt;
            &lt;TextBox 
                Text=&quot;{Binding SubTotal, Mode=TwoWay}&quot; 
                /&gt;

            &lt;TextBlock
                Text=&quot;Generosity&quot;
                Style=&quot;{StaticResource PhoneTextSubtleStyle}&quot;
                /&gt;
            &lt;Slider 
                Value=&quot;{Binding Generosity,Mode=TwoWay}&quot; 
                SmallChange=&quot;1&quot; 
                LargeChange=&quot;10&quot; 
                Minimum=&quot;0&quot; 
                Maximum=&quot;100&quot; /&gt;

            &lt;TextBlock
                Text=&quot;Tip&quot;
                Style=&quot;{StaticResource PhoneTextSubtleStyle}&quot;
                /&gt;
            &lt;TextBlock 
                Text=&quot;{Binding Tip}&quot; 
                /&gt;
        &lt;/StackPanel&gt;
    &lt;/Grid&gt;
</font></code></pre>
                                                <p>
                                                    <strong>Note</strong> that in XAML, <font color="#000000"><code>OneWay</code> binding is generally the default. To provide TwoWay binding we explicitly add <code>Mode</code> to our binding expressions: e.g. <code>Value=&quot;{Binding Generosity,Mode=TwoWay}&quot;</code></font></p>
                                                <p>
                                                    In the designer, this will look like:</p>
                                                <p>
                                                    <img alt="Designer" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_WP_Designer.png"></p>
                                                <h2>
                                                    The WP UI is complete!</h2>
                                                <p>
                                                    At this point you should be able to run your application.</p>
                                                <p>
                                                    When it starts... you should see:</p>
                                                <p>
                                                    <img alt="v1" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_WP_Emu.png"></p>
                                                <p>
                                                    This seems to work perfectly, although you may notice that if you edit the value in the <font color="#000000"><code>SubTotal</code> TextBox then you rest of the display does not correctly update.</font></p>
                                                <p>
                                                    This is a View concern - it is a UI problem. So we can fix it just in the WindowsPhone UI code - in this View. For example, to fix this here, you can add the &#39;Coding4Fun&#39; toolkit from Nuget and then use their <font color="#000000"><code>UpdateSourceOnChange</code> attached property to resolve the issue</font></p>
                                                <pre>
<code><font color="#000000"> coding4fun:TextBinding.UpdateSourceOnChange=&quot;True&quot;
</font></code></pre>
                                                <h2>
                                                    Moving on...</h2>
                                                <p>
                                                    There&#39;s more we could do to make this User Interface nicer and to make the app richer... but for this first application, we will leave it here for now.</p>
                                                <p>
                                                    Let&#39;s move on to even more Windows!</p>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<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