Reputation: 1102
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
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
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
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);
}
}
}
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.
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