Shell navigation reuses previous page in .NET MAUI

335 views Asked by At

I have a .NET MAUI app that uses a database and a shell tabbar for page navigation.

One of pages, "Clients", shows data from the database and be loaded every time the page is displayed.

I had an issue where when I move around pages on TabBar, the data retrieved from database kept stacking up on previous results on the "Clients" page every time I visit there.

I was able to solve this issue by adding this code that I found here:

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
    }

    ShellContent _previousShellContent;

    protected override void OnNavigated(ShellNavigatedEventArgs args)
    {
        base.OnNavigated(args);
        if (CurrentItem?.CurrentItem?.CurrentItem is not null &&
            _previousShellContent is not null)
        {
            var property = typeof(ShellContent)
                .GetProperty("ContentCache", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);

            property.SetValue(_previousShellContent, null);
        }

        _previousShellContent = CurrentItem?.CurrentItem?.CurrentItem;
    }
}

Then, I added a subpage "NewClientPage" that branches off this "Clients" page and is navigated with

ClientsPage.xaml.cs:

private async void AddClient_Clicked(object sender, EventArgs e){
   await Shell.Current.GoToAsync("NewClientPage");
}

App.xaml.cs:

Routing.RegisterRoute("NewClientPage", typeof(NewClientPage));

and goes back to "Clients" page with GoToAsync() (did not use built-in go back button because it did not work as I expected).

And now, It has the same issue of reusing previous page contents after going to NewClientPage, coming back to ClientsPage, and going to other page on TabBar.

For example,

  1. MainPage --> 2. ClientsPage --> 3. NewClientPage --> 4. ClientsPage --> 5. MainPage, --> 6. ClientsPage

where MainPage and ClientsPage are on a TabBar.

Right after coming back from NewClientPage at 4, ClientsPage looks good. However, if I go to other page and come back, newly loaded data is displayed under the previous page content as duplicates.

This is the code that loads the page. Referred to this video.

ClientsViewModel.cs:

public partial class ClientsViewModel : ObservableObject
{ 
   private readonly DatabaseContext _context;

   public ClientsViewModel(DatabaseContext context)
   {
     _context = context;
   }   

   public async Task LoadClientsAsync()
   {
     var clients = await _context.GetAllAsync<Client>();

     if (clients is not null && clients.Any())//if we have at least one client
     {
             Clients ??= new ObservableCollection<Client>(); //if null, initialize

             foreach (var client in clients)//insert each client into a obervable collection Clients 
             {
                 Clients.Add(client);
             }
      }
   }
}

This is called in OnAppearing().

ClientsPage.xaml:

 public partial class ClientsPage : ContentPage
{
  private readonly ClientsViewModel _viewModel;

  public ClientsPage(ClientsViewModel viewModel)
  {
    InitializeComponent();
    BindingContext = viewModel;
    _viewModel = viewModel;
  }

  protected async override void OnAppearing()
  {
    base.OnAppearing();
    await _viewModel.LoadClientAsync();
  }
}

When I debug, OnNavigated seems to be running, but there is still previous data left after using GoToAsync. When I'm on the NewClientPage, CurrentItem?.CurrentItem?.CurrentItem.Title is Client. I'm guessing there is something wrong when _previousShellContent == CurrentItem?.CurrentItem?.CurrentItem like Clients(on ClientsPage) == Clients(actually on NewClientPage), but I am not sure. I'm testing on Android.

How could I fix this?

Code snippets to reproduce the bug.

2

There are 2 answers

0
Ch1h0 On BEST ANSWER

I solved this by registering "NewClientPage" as <ShellContent> outside of <FlyoutItem> instead of using Routing.RegisterRoute.

Now I have Shell.Current.GoToAsync(//ClientsPage); for page navigation.

In AppShell.xaml

 <TabBar>
   ...
 </TabBar>
 <ShellContent ContentTemplate="{DataTemplate pages:NewClientPage}" Route="NewClientPage">
0
Jessie Zhang -MSFT On

You can try to clear the data before loading data again with code:

 Clients.Clear();

Please refer to the following code:

public partial class ClientsViewModel : ObservableObject
{      

      public async Task LoadClientsAsync() 
        {
            await ExecuteAsync(async () =>
            {
                var clients = await _context.GetAllAsync<Client>();
                Console.WriteLine("length = " + clients.LongCount());

                if (clients is not null && clients.Any())//if we have at least one client
                {
                    Clients ??= new ObservableCollection<Client>(); //if null, initialize
                   
                   //clear data before adding data again. 
                    Clients.Clear();


                    foreach (var client in clients)//insert each client into a obervable collection Clients 
                    {
                        Clients.Add(client);
                    }
                }
            }, "Fetching clients...");
        }
}