Sara can has MVVM – Part 1 – Writing a Xamarin.Forms App

I’m big into dev journaling. I don’t know why. I have OneNotes filled of “What did I learn today”, which are probably mostly wrong from random searches. I want to break out of this “Expert Beginner” mode. And the best way I can think of that right now is through transparency.

I have a couple of physical therapy iOS apps in the app store that I’m rewriting in Xamarin.Forms. I finished these over the summer, but I didn’t like the spaghetti code mess I inflicted upon myself. I’m taking this holiday break to re-re-design my app to fit a MVVM pattern. Part of me says “it’s better to have a working app in the app store than no app” and another part of me says “what will it take for me to grok non-spaghetti code concepts??” Those two parts have finally collided.

I’ve spoken with several people over the past 6 months to help me get over this MVVM conceptual hurdle, which you’ll read about in Part 2. To be clear, I get commanding and databinding. What I’ve always struggled with is how to get that data consumed by other parts of the app (e.g. a service, but how does a service work? Is it a static class? Is it a singleton? etc.)

Huge shout out and thank you to Matt Soucoup for being my human StackOverflow! Matt and I have been recording code review sessions together where I get to ask all of my n00b questions “Why are you using a NavigationPage and not just a regular ContentPage?”,  “Oh is that what that xmlns stands for!” and “I didn’t realize databinding took care of that automatically!” If you’re doing Xamarin work, stop reading my blog and go check out his at https://codemilltech.com/

Aside: Writing code is similar to writing an essay. There are so many different ways to express one’s thoughts. So many different ways to solve logic problems. So many different ways to code. Always fascinating to what someone else write code.

What I’m building

I’m rewriting “OnMyNerves” (a nerve desensitization therapy timer app – it’s really just a glorified timer). The user enters several Fabrics (that are used to desensitize the nerve) into a ListView. The user enters the Name and Seconds for each fabric. These fabrics are persisted on disk, so the user enters their “daily workout plan” only once.

This Fabric ListView is found under the Settings tab.

Mockup Showing list of fabrics

After the user populates the Settings, the user taps on the Timer page. When the user hits play, the timer starts for that fabric. In the screenshot below, Fabric 1 has 7 seconds remaining.

Mockup showing current Fabric timer progress

And the end, an alarm plays along with a couple of message boxes for user prompts (trust me the UX will make sense when we get there).

After the user dismisses the 2 message boxes, the timer goes onto the next fabric. When the timer has gone through all fabrics in the list, the sessions is saved (this is new in this Xamarin.Forms rewrite).

Each section below contains a “What I learned” retrospective.

  1. Creating a two page Tabbed control
  2. Writing up a ViewModel for Xamarin.Forms pages
  3. Adding XAML Compiling

To follow along for this blog post, check out all the tags (no pun intended) using git tag at   https://github.com/saraford/ListViewInsideTabbedControl

1 – Creating a two page Tabbed control

To see the completed code for this step:

​​​​​​git checkout tags/two-page-tabbed-control

Let’s start with the App()  constructor.

public App()  {

            InitializeComponent();

            var page1 = new Page1() {
                // needed to give the tabbed page a name
                Title = "Page 1"
            };
            var page1Nav = new NavigationPage(page1);

            var page2 = new Page2() {

                // needed to give the tabbed page a name
                Title = "Page 2"
            };
            var page2Nav = new NavigationPage(page2);

            // look! no corresponding .xaml file
            var tabbed = new TabbedPage();
            tabbed.Children.Add(page1);
            tabbed.Children.Add(page2);
            MainPage = tabbed;
}

This code will produce the following app:

Initial 2 pages in a Tabbed control non-MVVM

What did I learn?

  • If you look in the solution, you’ll discover that I’ve deleted the ListViewInsideTabbedDocumentPage.xaml file. Turns out you don’t need a “physical” .xaml file to go along with your tabbed page. I was surprised by this, asking, “But if I get rid of the .xaml file, that implies I’ll never have to make any XML changes?” Well, it turns out this is okay. The tabbed page really is a ‘holder’ of other pages, and doesn’t have a much of a UI other than the tabs. So in this case it is a lot easier to create the tabbed page all in C# rather than using XAML. You can just queue it up in a code behind. You can do more UX stuff with C# than you can do in XAML, but you’d really have to be getting into it at that point.
  • I also learned that you can have interactive tabs that don’t have text or images! So if you’re wondering, “why aren’t my tabs showing up?” They might be, even if you didn’t specify a Title or Image. Just look closely or tap 🙂
  • You’ll read about NavigationPage vs just new’ing up a ContentPage tomorrow

2 – Wiring up the ViewModel for our Xamarin.Forms pages

To see the completed code for this step:

git checkout tags/viewmodel-created

I remember someone telling me years ago that you always want to use a MVVM framework. This section explains why using a MVVM framework helps prevent you from rewriting code. BTW I’m the *worst* when it comes to this…I rewrite things, crazy things, all the time because I never stop first to check if there’s something I could reuse.

Step 1 – install Refactored.MvvmHelpers by James Montemagno. Install this in your PCL.

nuget installing Refactored.MvvmHelpers

This MvvmHelpers library gets you a couple of useful things

  • ObservableObject – you’ll see this more when we start to work on the ListView, but you know how you have to implement INotifyPropertyChange and use properties for databinding? That’s what this class does for you! No more hours wasted trying to debug “why isn’t my data refreshing?”

Looking at ObservableObject class

  • BaseViewModel – In a few minutes, you’ll have your ViewModels inherit from this class. BaseViewModel gives your ViewModels some common properties that will be used in practically all views, like Title and IsBusy. All you need to do is databind them. But what’s even better is that BaseViewModel also derives from ObservableObject, so again, you don’t have to redo INotifyPropertyChange in each of your ViewModels.

Step 2 – Next let’s refactor our solution

  • Move Page1.xaml and Page2.xaml into a new folder called Views. Next don’t forget to update the namespace from ListViewInTabbedDocument to ListViewInTabbedDocument.Views
    • This gets me every time “I moved the file but yet VS still knows where to find it!” So rename your namespace Every. time. sigh.
    • the namespace in the xaml, e.g.  x:Class=”ListViewInTabbedDocument.Views.Page1″>
  • Create a new folder call ViewModels. Add two classes, PageOneViewModel.cs and PageTwoViewModel.cs, creatively, into this ViewModels folder. (and please please please let Ready Play One movie be good!)

Step 3 – Have the PageOneViewModel look like the code below

Note the Navigation class in the constructor. You’ll pass this Navigation class in from the View so that the ViewModel will be able to call Push and Pop. More on that later.

Yes, I’m putting the View’s Navigation property into the view model… not exactly a pure separation of concerns. But this actually works out quite nicely! And it’s my next blog post (Part 2) that I’m desperately trying to grok, i.e. how the TimerPage gets the databinding’ed (databound? databinded?) data.

Also note the Title property being set. This comes from BaseViewModel.

using MvvmHelpers;
using Xamarin.Forms;

namespace ListViewInTabbedDocument.ViewModels {

    class PageOneViewModel : BaseViewModel  {

        private INavigation Navigation;

        public PageOneViewModel(INavigation Navigation) {

            Title = "Page One from VM";
            this.Navigation = Navigation;
        }
    }
}

Aside: my first CS prof told us never use the same variable name as in the parameter of a method or constructor as the class fields / properties. But this in the days of Turbo Pascal and I don’t recall if we had a `this` keyword back then.

Step 4 – Let’s have the Views create their ViewModels

The ViewModels will need to pass in the Navigation and setup the ViewModel as the binding context. So in the Views code behind…

public Page1 () {

   InitializeComponent();

   var pageOneViewModel = new PageOneViewModel(this.Navigation);
   this.BindingContext = pageOneViewModel;
}

Step 5 – Use databinding for the Title

Now to test we’re actually getting content from the ViewModel binding, update the Page1.xaml as follows

<ContentPage.Content>
        <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
            <Label Text="{Binding Title}" />
        </StackLayout>
</ContentPage.Content>

And you’ll get the following. Notice the new text.

2 Pages using MVVM

Rinse and repeat for Page2.

The reason why I like doing things twice by having two pages (and yeah it’s a moot point since a tabbed control needs 2 or more) is that I can verify my blog posts by following my own directions for Page2.

What did I learn

  • First and more importantly, Alt+Enter in Visual Studio for Mac is the equivalent of Ctrl+. in Visual Studio.
  • I finally had a chance to ask all my embarrassing questions, including “When do you need to put the assembly name in the namespace? Is there something special about the name `local`? And what exactly is `xmlns`?   For example…”
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:ex="clr-namespace:OnMyNerves.Views;assembly=OnMyNerves"
    x:Class="OnMyNerves.Views.SettingsPage"

Here’s the breakdown…

  • `x:Class` is for the code behind
  •  “xmlns:x” which is the standard XAML namespace – which defines things like x:Name
  • “xmlns:local” is used when you’re going to use something in a different class somewhere that’s not in code behind. There’s nothing special re local. In this example, I’m using “xmlns:ex”.
    • Usually you’ll see both the `clr-namespace` and `assembly` defined
    • Wouldn’t need assembly if it is in the same an assembly, but if it is part of a 3rd party control, etc. you have to have it. Never hurts to be explicit.
  • “xmlns” – this stands for “XAML Namespace”, akak the base namespace. Means you can get everything that’s available in the Xamarin Forms namespace without any prefix, like StackLayout. Otherwise, prefixing these controls all over the place would get really old really fast.
  • “x.Name” – that’s part of the overall xaml namespace, which is different from the base namespace.

The more you know!

3 – XAML Compiling

To follow along:

git checkout tags/xaml-compiling

In App.xaml.cs, add the assembly attribute before the namespace.

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace ListViewInsideTabbedDocument {

What I learned

  • When it is on, in the final package XAML is complied down to IL. Otherwise, it would have to inflate and parse at runtime.
  • And XAML compiling does help during debugging … it will throw compile errors if you have any errors in your XAML (like typos) – that way you don’t have to wait until you actually load up the page at debug-time before it crashes on you.

Next blog post is about how to wire up a ListView instead the TabbedControl. Not sure why I called my project ListViewInsideTabbedDocument – that’s some old muscle memory from Visual Studio taking over my typing!

One thought on “Sara can has MVVM – Part 1 – Writing a Xamarin.Forms App

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s