Reusing ViewModels in a Universal App – Part 1
There is an increasing trend for Microsoft platforms to build what is called a “Universal App”. In its ideal it is an application that you code once, and then it runs on desktop, tablet and phone platforms.
This opportunity introduces new challenges – with a main one being that the different platforms have differences. Differences in APIs, differences in capabilities and differences in display size and orientation.
All of these threaten the ideal of reusing most, if not all, of the code between the platforms. The less code you can share between the platforms, means:
· A higher cost for development – meaning either more resources needed, or more time required
· More code that needs to be maintained
· Higher probability of bugs, especially that impact one platform but another
The Model-View-ViewModel pattern has the potential to increase code reuse across the platforms. This can be done by minimizing logic in the View and designing a View Model that is agnostic of the details of the View. This will allow us to develop a different View for the different platforms, while sharing the code in the ViewModel and Model.
To illustrate this I’ve created a Windows Store application, called My Calc, which is purposeful coded in a naive View centric way. As is if one were to make the application available phone would be a large incremental cost.
MyCalc is a simple stack calculator application. One can push numbers on the stack. Operations, such as Add, take their operands from the stack and replace it with the resulting value. Purposefully the set of operations implemented as basic, as the object is to focus on ViewModel development, and not develop a fully featured calculator app.
In the various parts of this blog we’ll first refactor the application to have a ViewModel, and end with porting the application to phone by coding up a phone specific View.
Attached is our starting solution. It consists of 3 projects:
· MyCalc.Shared
o Contains code shared across the platforms
· MyCalc.Windows
o Contains code specific to Windows
· MyCalc.WindowsPhone
o Contains code specific to phone
The shared project contains a StackCalculator class. This is our Model, or business logic. It doesn’t know anything about the UI.
You can ignore the WindowsPhone project for now, as all it has in it is the broiler plate code the Visual Studio put in when the project was created.
The Windows project has the MainWindow. If you’ve done UI development in XAML, WPF or Silverlight then likely you’ve seen (and written) code similar to this.
Here is a snippet of the xaml
<Button
x:Name="SixButton"
Grid.Column="4"
Grid.Row="2"
Style="{StaticResource ButtonStyle}"
Content="6"
Click="SixButton_Click"
/>
<Button
x:Name="ThreeeButton"
Grid.Column="4"
Grid.Row="3"
Style="{StaticResource ButtonStyle}"
Content="3"
Click="ThreeeButton_Click"
/>
</Grid>
<Grid
x:Name="StackGrid"
Grid.Column="1"
Grid.Row="1"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox
x:Name="StackBox"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="0"
Margin="4"
FontSize="32"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
/>
<TextBox
x:Name="EnterBox"
Grid.Column="0"
Grid.Row="1"
Margin="4"
Width="400"
FontSize="32"
TextChanged="EnterBox_TextChanged"
/>
There are a few things I’d like to point out about this code:
· The elements (buttons, ListBox, TextBox, etc…) have names so we can reference them in the code behind
· Events are wired up to methods in the code behind, such as the Clicked and TextChanged events.
These are indicators that this UI was coded up in a traditional way without a ViewModel (or at least not a well designed one). When we look at the code we get that confirmed with code like this:
privatevoid EnterBox_TextChanged(object sender, TextChangedEventArgs e)
{
UpdateEnterBoxButtons();
}
privatevoid UpdateEnterBoxButtons()
{
if (EnterBox.Text.Length == 0)
{
EnterButton.IsEnabled = false;
BackspaceButton.IsEnabled = false;
}
else
{
EnterButton.IsEnabled = true;
BackspaceButton.IsEnabled = true;
}
}
Here we see that the logic in the View is very tightly coupled with the XAML markup, since the View not only responds to events from the controls, but directly updates the state of various controls.
While one could theoretically compile this code against 2 sets of markup (and there are some challenges to just making that happen) we are quite limited in the changes that could be made – other than just layout and style changes. For example the View code expects both an Enter and Backspace button – but what if, due to space constraints, on phone we would rather just use the ones on the soft keyboard rather than have such buttons in our main UI.
Comments
Anonymous
August 26, 2014
Ohhh, so beautiful formatted code.Anonymous
August 28, 2014
Thanks David :) I've been working on how to format the code better, and have just updated the post with the improvements. Hope you like the formatting and, of course, the article series.Anonymous
August 29, 2014
Awesome series of posts that really illustrates the practical value of an MVVM structure.