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,
- 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.
I solved this by registering "NewClientPage" as
<ShellContent>
outside of<FlyoutItem>
instead of usingRouting.RegisterRoute
.Now I have
Shell.Current.GoToAsync(//ClientsPage);
for page navigation.In AppShell.xaml