Reputation: 7391
I have a ContentPage
which requires some "heavy lifting" before its data can be shown.
So, my idea was to have an ActivityIndicator
on the page visible until the data is ready to be displayed.
I'm trying to figure out a suitable event to use for this purpose.
I can't use the Appearing
event as that happens right before the page becomes visible.
Same goes for events like Loaded
or NavigatedTo
, as they all fire before the page becomes visible.
So, my question is: is there a suitable event for performing some long-running task after a page has loaded and become visible to the user?
Edit: The long-running operation is async
, but it seems the data-binding to a CollectionView
on the page is what is causing perceived lag when the page loads, which is why I'd like to hold off on data-binding until the page is visible with the ActivityIndicator
spinning.
Edit 2: I might have been chasing a red herring. The UI stutters seem to be caused by some SVGs that were loaded in the CollectionView
template. Not sure why, perhaps related to the PNG conversion. Either way, I don't think I actually need the type of event I was originally looking for.
Upvotes: 11
Views: 17529
Reputation: 8866
One approach that I sometimes use for this is the following.
The heavy-lifting (loading) goes into your ViewModel, but you get a handle to the LoadAsync() method before even instantiating the View. You can bind your ActivityIndicator to an IsBusy property. Once everything is loaded, you can hide the ActivityIndicator.
ViewModel
private bool _isBusy;
public bool IsBusy
{
get => _isBusy;
set
{
_isBusy = value;
OnPropertyChanged();
}
}
public async Task LoadAsync()
{
IsBusy = true;
//Some long running operation
IsBusy = false;
}
View
<ActivityIndicator IsRunning="{Binding IsBusy}"/>
Navigation
In the code where you instantiate the View and ViewModel, do the following:
var viewModel = new ViewModel();
var loadTask = viewModel.LoadAsync();
await Navigation.PushAsync(new View(viewModel));
await loadTask;
Or simplified as:
var viewModel = new ViewModel();
await Navigation.PushAsync(new View(viewModel));
await viewModel.LoadAsync();
Do not set the Binding
for the ItemsSource property in XAML, but only in the code-behind, like follows:
Code Behind
private readonly ViewModel _vm;
public View MyView(ViewModel vm)
{
InitializeComponent();
BindingContext = _vm = vm;
LoadAfterConstruction();
}
private async void LoadAfterConstruction()
{
_vm.IsBusy = true;
await _vm.LoadAsync();
//only set the binding of the CollectionView after loading completed
MyCollectionView.SetBinding(ItemsView.ItemsSourceProperty, nameof(MyCollection));
_vm.IsBusy = false;
}
XAML:
<CollectionView x:Name="MyCollectionView" />
In either case, your view becomes visible and then loading sets in.
Note: This code is not written for optimization or MVVM-correctness.
Upvotes: 12
Reputation: 7391
This was a red herring.
The problem wasn't related to CollectionView
, but rather the SVG images (icons) that were rendered on the page.
Because of the SVG's having large view boxes, the Resizetizer in .NET MAUI converted them to very large PNGs, which when rendered caused the UI to become choppy.
Setting BaseSize
attributes to more reasonable icon sizes on the <MauiImage />
elements in .csproj
made the UI snappy.
Upvotes: 5