Click here to Skip to main content
15,038,469 members
Articles / Mobile Apps / Xamarin
Article
Posted 29 Jun 2016

Tagged as

Stats

19.7K views
5 bookmarked

Async Xamarin Forms MVVM Model

Rate me:
Please Sign up or sign in to vote.
2.84/5 (10 votes)
29 Jun 2016CPOL1 min read
Async MVVM model template for Xamarin Forms

Image 1

Introduction

This article is about async MVVM model for Xamarin Forms.

Background

While playing with Xamarin Forms, the problem I've had is async operations on first page load. We cannot use async in constructor properly, so what I need is an async call after page load. A method come to my mind and it's working without problem, and I wanted to share it. I hope you like it.

I'll write everything from scratch. These are the current versions I've used:

  • Visual Studio 2015 Community Edition
  • Xamarin for Visual Studio (4.1.1)

Using the Code

Let's create a project and add our base model and use it. It'll take just three steps.

You can view the source code in GitHub.

1. Create a New Xamarin Forms Project (Xaml App)

Image 2

And we're ready.

Image 3

2. Create Base Model

Image 4

C#
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace AsyncTest.Models
{
    internal abstract class BaseModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

For async operations, this base model has to know which page it is bind to. So let's add it:

C#
protected Page CurrentPage { get; private set; }

Now, we need an initializer:

C#
public void Initialize(Page page)
{
    CurrentPage = page;

    CurrentPage.Appearing += CurrentPageOnAppearing;
    CurrentPage.Disappearing += CurrentPageOnDisappearing;
}

The complete code is as follows:

C#
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace AsyncTest.Models
{
    internal abstract class BaseModel : INotifyPropertyChanged
    {
        protected Page CurrentPage { get; private set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public void Initialize(Page page)
        {
            CurrentPage = page;

            CurrentPage.Appearing += CurrentPageOnAppearing;
            CurrentPage.Disappearing += CurrentPageOnDisappearing;
        }

        protected virtual void CurrentPageOnAppearing(object sender, EventArgs eventArgs) {}

        protected virtual void CurrentPageOnDisappearing(object sender, EventArgs eventArgs) {}

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

As you see, base model takes page as property and creates virtual methods for "Appearing" and "Disappearing" events. If you need other events, just add them as virtual methods.

3. Create Page and Use It

We already have "MainPage.xaml", so let's use it.

First, we need a model for page.

C#
namespace AsyncTest.Models
{
    internal class MainModel : BaseModel {}
}

I'll use http://jsonplaceholder.typicode.com/ for testing.

C#
using System;
using System.Net.Http;

namespace AsyncTest.Models
{
    internal class MainModel : BaseModel
    {
        private string _responseText;

        public string ResponseText
        {
            get { return _responseText; }
            set
            {
                _responseText = value;
                OnPropertyChanged();
            }
        }

        protected override async void CurrentPageOnAppearing(object sender, EventArgs eventArgs)
        {
            ResponseText = "Loading, please wait...";
            
            try
            {
                // Wait for testing...
                await Task.Delay(TimeSpan.FromSeconds(3));
                
                using (var client = new HttpClient())
                {
                    var responseMessage = await client.GetAsync
                                          ("http://jsonplaceholder.typicode.com/posts/1");

                    if (responseMessage.IsSuccessStatusCode)
                        ResponseText = await responseMessage.Content.ReadAsStringAsync();
                    else
                        ResponseText = $"StatusCode: {responseMessage.StatusCode}";
                }
            }
            catch (Exception exception)
            {
                ResponseText = exception.ToString();
            }
        }
    }
}

I like intellisense in designer so I'll add model into designer.

XML
<?xml version="1.0" encoding="utf-8"?>

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:models="clr-namespace:AsyncTest.Models;assembly=AsyncTest"
             x:Class="AsyncTest.MainPage">

    <ContentPage.BindingContext>
        <models:MainModel />
    </ContentPage.BindingContext>

    <ContentPage.Content>
        <Label Text="{Binding ResponseText}" />
    </ContentPage.Content>

</ContentPage>

And now we have to tell the model about this page so it'll initialize events.

C#
using AsyncTest.Models;

namespace AsyncTest
{
    public partial class MainPage
    {
        public MainPage()
        {
            InitializeComponent();
            ((BaseModel) BindingContext).Initialize(this);
        }
    }
}

That's it. Hit F5 and enjoy!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Efe Erdoğru
Software Developer (Senior)
Turkey Turkey
No Biography provided

Comments and Discussions

 
QuestionSeriously? Pin
n.podbielski7-Jul-16 23:46
Membern.podbielski7-Jul-16 23:46 
1. MVVM? Where there is a view model in your code? I see only view and model.
https://i-msdn.sec.s-msft.com/dynimg/IC564167.png[^]
2.
For async operations, this base model has to know which page it is bind to"

No i do not have to. You can as easily create simple async method in view model and call it from view in OnAppearingMethod.
protected override void OnAppearing()
        {
            base.OnDisappearing();
            ViewModel.Test();
        }
public async void Test()
        {
            await Task.Delay(10000);
        }

Handling events from view in view model? It suppose to work other way around: view should show changes from view model.
Best way IMHO is to bind events to commands: there is only need to create attached property once and you can bind it in view via Xaml binding mechanism anywhere.
Your protected event handler? You can't even test it properly...

3.
I like intellisense in designer so I'll add model into designer.


So this is how this bad practice is redistributed around? I am not an expert in Android and iOS development but I am quite sure that there is no XAML in those environments. So not everybody who start using Xamarin know how Xaml works.
Not that long time ago I encountered something strange like that in buggy code written by colleague. He asked my what is wrong. View model just had a constructor parameter. Just like that it only take one parameter for this code to stop working. And you will do not get any errors in compile time.
Please stop teaching people to write code like that. I will give you 2 because it works and you shared it with others. Beside that it is just bad. And nominated for best article of month... OMG.
No more Mister Nice Guy... >: |

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.