Ted Nyberg
Ted Nyberg

Reputation: 7391

Handle event after .NET MAUI page has become visible

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

Answers (2)

Julian
Julian

Reputation: 8866

Option 1

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();

Option 2: Defer Data Binding

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" />

https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/populate-data#populate-a-collectionview-with-data

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

Ted Nyberg
Ted Nyberg

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

Related Questions