|
<h1>
MvvmCross TipCalc - Step5 Creating a Windows Store UI</h1>
<h2>
Introduction</h2>
<p>
This article is step 5 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">
<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 'Core' Portable Class Library project which contained:</p>
<ul>
<li>
our 'business logic' - <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've then three User Interfaces - for Xamarin.Android, Xamarin.iOS and WindowsPhone:</p>
<p>
<img alt="Android" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_Android_Styled.png"><img alt="v1" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_Touch_Sim.png"><img alt="v1" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_WP_Emu.png"></p>
<p>
For our next project, let's shift to WindowsStore.</p>
<p>
To create a WindowsStore MvvmCross UI, you can use the Visual Studio project template wizards, but here we'll instead build up a new project 'from empty', just as we did for the Core, Android, iOS and WindowsPhone projects.</p>
<p>
Obviously, to work with WindowsStore, you will need to be working on the PC with Visual Studio</p>
<h2>
Create a new WindowsStore Project</h2>
<p>
Add a new project to your solution - a 'Blank App (XAML)' application with name <code><font color="#000000">TipCalc.UI.WindowsStore</font></code></p>
<p>
Within this, you'll find the normal WindowsStore application constructs:</p>
<ul>
<li>
the 'Assets' folder</li>
<li>
the 'Common' folder</li>
<li>
the 'Properties' folder with just the 'AssemblyInfo' file</li>
<li>
the App.Xaml 'application' object</li>
<li>
the MainPage.Xaml and MainPage.Xaml.cs files that define the default Page for this app</li>
<li>
the 'Package.appxmanifest' configuration file</li>
<li>
the debug private key for your development</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>
</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 - WindowsStore specific versions</h3>
<p>
Add references to the new project for the WindowsStore specific libraries:</p>
<ul>
<li>
<strong>Cirrious.CrossCore.WindowsStore.dll</strong></li>
<li>
<strong>Cirrious.MvvmCross.WindowsStore.dll</strong></li>
</ul>
<p>
Each of these extends the functionality of its PCL counterpart with WindowsStore specific additions.</p>
<p>
Normally these will be found in a folder path like <em>{SolutionRoot}/Libs/Mvx/WindowsStore/</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, iOS and WO 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 WindowsStore UI project all you have to supply is:</p>
<ul>
<li>
your <font color="#000000"><code>App</code> - your link to the business logic and <code>ViewModel</code> content</font></li>
</ul>
<p>
For <font color="#000000"><code>TipCalc</code> here's all that is needed in Setup.cs:</font></p>
<pre>
<code><font color="#000000">using Cirrious.MvvmCross.ViewModels;
using Cirrious.MvvmCross.WindowsStore.Platform;
using Windows.UI.Xaml.Controls;
namespace TipCalc.UI.WindowsStore
{
public class Setup : MvxStoreSetup
{
public Setup(Frame rootFrame) : base(rootFrame)
{
}
protected override IMvxApplication CreateApp()
{
return new Core.App();
}
}
}
</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 WindowsStore 'main application' object - an object which owns the User Interface and receives some callbacks from the operating system during some key events in your application'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 <font color="#000000"><code>OnLaunched</code> callback</font></p>
<ul>
<li>
<p>
remove these lines</p>
<pre>
<code><font color="#000000"> if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
{
throw new Exception("Failed to create initial page");
}
</font></code></pre>
</li>
<li>
<p>
add these lines to allow it to create <font color="#000000"><code>Setup</code>, and to then initiate the <code>IMvxAppStart</code> <code>Start</code> navigation</font></p>
<pre>
<code><font color="#000000">var setup = new Setup(RootFrame);
setup.Initialize();
var start = Mvx.Resolve<IMvxAppStart>();
start.Start();
</font></code></pre>
</li>
</ul>
</li>
</ul>
<p>
To do this, you will need to add these <font color="#000000"><code>using</code> lines:</font></p>
<pre>
<code><font color="#000000">using Cirrious.CrossCore.IoC;
using Cirrious.MvvmCross.ViewModels;
</font></code></pre>
<h2>
Add your View</h2>
<h3>
Create an initial Page</h3>
<p>
Create a Views folder</p>
<p>
Within this folder, add a new 'Basic Page' and call it <code><font color="#000000">TipView.xaml</font></code></p>
<p>
You will be asked if you want to add the missing 'Common' files automatically in order to support this 'Basic Page' - answer <strong>Yes</strong></p>
<p>
The page will generate:</p>
<ul>
<li>
TipView.xaml</li>
<li>
TipView.xaml.cs</li>
</ul>
<p>
Within Common you will also have new files added:</p>
<ul>
<li>
BindableBase.cs</li>
<li>
BooleanNegationConverter.cs</li>
<li>
BooleanToVisibilityConverter.cs</li>
<li>
LayoutAwarePage.cs</li>
<li>
RichTextColumns.cs</li>
<li>
SuspensionManager.cs</li>
</ul>
<h3>
Convert LayoutAwarePage into an MvvmCross base view</h3>
<p>
Change <font color="#000000"><code>LayoutAwarePage</code> so that it inherits from <code>MvxStorePage</code></font></p>
<p>
Change:</p>
<pre>
<code><font color="#000000">public class LayoutAwarePage : Page
</font></code></pre>
<p>
to:</p>
<pre>
<code><font color="#000000">public class LayoutAwarePage : MvxStorePage
</font></code></pre>
<p>
This requires the addition of:</p>
<pre>
<code><font color="#000000">using Cirrious.MvvmCross.WindowsStore.Views;
</font></code></pre>
<h3>
Persuade LayoutAwarePage to cooperate more reasonably with the <code><strong>MvxStorePage</strong></code> base class</h3>
<p>
Either remove the <font color="#000000"><code>region</code>:</font></p>
<pre>
<code><font color="#000000"> #region Process lifetime management
// all sorts of 'stuff' including
// OnNavigatedTo
// OnNavigatedFrom
// LoadState
// SaveState
#endregion
</font></code></pre>
<p>
Or change the <font color="#000000"><code>OnNavigatedTo</code> and <code>OnNavigatedFrom</code> methods so that they call their base class implementations:</font></p>
<pre>
<code><font color="#000000">base.OnNavigatedTo(e);
</font></code></pre>
<p>
and</p>
<pre>
<code><font color="#000000">base.OnNavigatedFrom(e);
</font></code></pre>
<h3>
Turn TipView into the MvvmCross View for TipViewModel</h3>
<p>
Open the TipView.cs file.</p>
<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, Xamarin.iOS and WindowsPhone:</font></p>
<pre>
<code><font color="#000000">public new TipViewModel ViewModel
{
get { return (TipViewModel) base.ViewModel; }
set { base.ViewModel = value; }
}
</font></code></pre>
<p>
Remove the <font color="#000000"><code>LoadState</code> and <code>SaveState</code> methods.</font></p>
<p>
Altogether this looks like:</p>
<pre>
<code><font color="#000000">using TipCalc.Core.ViewModels;
using TipCalc.UI.WindowsStore.Common;
namespace TipCalc.UI.WindowsStore.Views
{
public sealed partial class TipView : LayoutAwarePage
{
public new TipViewModel ViewModel
{
get { return (TipViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
public TipView()
{
this.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>
Just as with the WindowsPhone, I won't go into much depth at all here about how to use the XAML or do the Windows data-binding. I'm assuming most readers are already coming from at least a little XAML background.</p>
<p>
To add the XAML user interface for our tip calculator, we will add a <font color="#000000"><code>ContentPanel</code> Grid just above the existing <code><VisualStateManager.VisualStateGroups></code> XAML node.</font></p>
<p>
This <font color="#000000"><code>Content Panel</code> will include <strong>almost</strong> exactly the same XAML as we added to the WindowsPhone example - only the <code>Style</code> attributes are removed:</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"> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<TextBlock
Text="SubTotal"
/>
<TextBox
Text="{Binding SubTotal, Mode=TwoWay}"
/>
<TextBlock
Text="Generosity"
/>
<Slider
Value="{Binding Generosity,Mode=TwoWay}"
SmallChange="1"
LargeChange="10"
Minimum="0"
Maximum="100" />
<TextBlock
Text="Tip"
/>
<TextBlock
Text="{Binding Tip}"
/>
</StackPanel>
</Grid>
</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="{Binding Generosity,Mode=TwoWay}"</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_Store_Designer.png"></p>
<h2>
The Store 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="Designer" src="https://raw.github.com/slodge/MvvmCross/v3/v3Tutorial/Pictures/TipCalc_Store_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 WindowsStore UI code - just as we did in the WindowsPhone example.</p>
<h2>
Moving on...</h2>
<p>
There'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's move on to one final piece of Windows!</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<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.