kokokok
kokokok

Reputation: 1102

When to load the ViewModel for asynchronous data in a Xamarin Android app?

When can I load a viewmodel data if the service is asynchronous?

I use MVVM Light in a Xamarin Android app. The viewmodel needs to load data from SqlLite in an asynchronous operation. Something like

class MyViewModel
{
     public Task Initialize()
     {
          Records = await _database.LoadData();
     }
     ...

I am not clear when I need to call Initialize() and if I need to wait for the completion of the loading.

Upvotes: 0

Views: 6235

Answers (3)

srmrco
srmrco

Reputation: 156

You might try it this way using Xamarin.CommunityToolkit:

MyViewModel.cs:

using Xamarin.CommunityToolkit.ObjectModel;
...


public ICommand LoadCommand { get; protected set; }

public MyViewModel()
{
    LoadCommand = new AsyncCommand(async () =>
    {
        // load data async
    });
}

Page.xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             xmlns:vm="clr-namespace:MyProject.ViewModels">

<ContentPage.BindingContext>
    <vm:MyViewModel />
</ContentPage.BindingContext>

<ContentPage.Behaviors>
    <xct:EventToCommandBehavior EventName="Appearing" Command="{Binding LoadCommand}" />
</ContentPage.Behaviors>

...

Upvotes: 4

puko
puko

Reputation: 2970

Another good way is to use an EventToCommandBehavior. Imagine that you want to load data from web server.

Make Command in your viewmodel like

LoadCommand = new Command(async () =>
{
    var result =  await service.GetYourObjectsAsync();
    Items = new ObservableCollection<YourObject>(result);
});


<ContentPage>
  <ContentPage.Behaviors>
    <local:EventToCommandBehavior EventName="Appearing" Command="{Binding LoadCommand }">
    </local:EventToCommandBehavior>
 </ContentPage.Behaviors>
</ContentPage>

When page will raise apperaring event your load commad raise and you can do anything what you want in command action ...

Upvotes: 1

Florian Moser
Florian Moser

Reputation: 2663

Assuming you know about ObservableCollection and the consequences of using async void (see my comments down below), I'd recommend one of these ways:

The easier way is to simply call a InitializeAsync() method to start loading in the constructor:

class MyViewModel
{
    public MyViewModel()
    {
        InitializeAsync();
    }

    //warning: async void! 
    public async void InitializeAsync()
    {
        Records = await _database.LoadData();
    }
}

Or you could adapt the _database Service to do the lazy loading (I recommend this method, as it keeps the ViewModel clean):

class MyViewModel
{
    public MyViewModel()
    {
        Records = _database.LoadData();
    }
}

class Database
{
    private ObservableCollection<Record> _data = new ObservableCollection<Record>();
    public ObservableCollection<Record> LoadData()
    {
        EnsureLoaded();
        return _data;
    }

    private bool _isLoaded = false;
    private async void EnsureLoaded()
    {
        lock (this)
        {
            if (_isLoaded)
                return;

            _isLoaded = true;
        }

        //do the actual loading here
        var myResultList = await DoLoadingAsync();
        foreach (myResultList as item)
        {
            _data.Add(item);
        }
    }
}

Notes about async void:

You can use void (instead of Task) as a return type of an asynchronous method. This will result in a "fire-and-forget" action:

public void DoStuff()
{
    FireAndForgetAsync();
}

private async void FireAndForgetAsync()
{
    await Task.Delay(1000);
    throw new Exception(); //will be swallowed
}

As you are returning void, you can not await FireAndForgetAsync. You will not be able to know when the method finishes, and any exception raised inside the async void method will be swallowed.

Notes about ObservableCollection:

This type of List raises an event once you add / remove / replace items. You can use this events to fill your view without having to reload the list as a whole each time it changes. If you implement this correctly you can show the user each item as soon as it finished loading, instead having him to wait until everything is loaded fully.

Upvotes: 3

Related Questions