I have a Maui application with tabs. On the DashboardMainPage, items are displayed in a CollectionView. From this page, I can navigate to a subpage (GoalsPage) where all items can be seen in more detail. In GoalsPage, I can navigate to another subpage (CreateGoalPage) where new goals can be saved to the local database.
The navigation flow (using shell navigation, Shell.Current.GoToAsync):
DashboardMainPage -> GoalsPage -> CreateGoalPage
public partial class DashboardMainViewModel : BaseViewModel
{
// The repository to handle CRUD to local db
private readonly IUnitOfWork _unitOfWork;
public ObservableCollection<Goal> Goals { get; } = new ObservableCollection<Goal>();
public DashboardMainViewModel(IUnitOfWork unitOfWork, INavigationService navigationService) : base(navigationService)
{
_unitOfWork = unitOfWork;
}
// This is called from OnAppearing of DashboardMainPage
internal async Task InitializeAsync()
{
if (IsInitialized) // Prevents LoadDataAsync running multiple times when navigate on and navigate back from subpage
{
return;
}
await LoadDataAsync();
}
private async Task LoadDataAsync()
{
Goals.Clear();
var goalsTask = _unitOfWork.Goal.GetAllAsync(_ => _.Completed == false);
Goals.AddRange(goalsTask);
}
[RelayCommand]
private async Task GoToGoalsPage() => await NavigationService.GoToAsync(nameof(GoalsPage));
}
public partial class GoalsViewModel : BaseViewModel
{
private readonly IUnitOfWork _unitOfWork;
public ObservableCollection<Goal> OnGoingGoals { get; } = new ObservableCollection<Goal>();
public ObservableCollection<Goal> CompletedGoals { get; } = new ObservableCollection<Goal>();
public GoalsViewModel(IUnitOfWork unitOfWork, INavigationService navigationService) : base(navigationService)
{
_unitOfWork = unitOfWork;
}
// This is called from OnAppearing of DashboardMainPage
internal async Task InitializeAsync()
{
if (IsInitialized) // Prevents LoadDataAsync running multiple times when navigate on and navigate back from subpage
{
return;
}
await LoadDataAsync();
}
private async Task LoadDataAsync()
{
OnGoingGoals.Clear();
CompletedGoals.Clear();
var goals = await _unitOfWork.Goal.GetAllAsync();
var completed = goals.Where(_ => _.IsCompleted).OrderByDescending(_ => _.Completed.Date);
OnGoingGoals.AddRange(goals.Where(_ => !_.IsCompleted));
CompletedGoals.AddRange(completed);
}
[RelayCommand]
private async Task GoToGoalCreatePage()
{
var goal = new Goal();
var parameters = new Dictionary<string, object> {
{ nameof(Goal), goal }
};
await NavigationService.GoToAsync(nameof(GoalCreatePage), parameters);
}
}
[QueryProperty(nameof(Goal), nameof(Goal))]
[QueryProperty(nameof(IsEdit), nameof(IsEdit))]
public partial class GoalCreateViewModel : BaseViewModel
{
// The repository to handle CRUD to local db
private readonly IUnitOfWork _unitOfWork;
[ObservableProperty]
private Goal _goal;
public GoalCreateViewModel(IUnitOfWork unitOfWork, INavigationService navigationService) : base(navigationService)
{
_unitOfWork = unitOfWork;
}
[RelayCommand]
private async Task Save()
{
var savedGoal = await _unitOfWork.Goal.SaveAsync(Goal);
if (savedGoal is not null)
{
// should I trigger here a weakreference to call item in the both parent vms?
// Navigate back to GoalsPage
await NavigationService.GoBackAsync();
}
}
}
QUESTIONS
When a new goal is added in the sub-sub page, how is it possible to update the two parent pages?
- Should I use weakreference (seems against mvvm)?
- The fact I am using twice the same list, is there a way I can make a single observable collection which can be reached in all VM? (DashboardMainViewModel , GoalsViewModel and CreateGoalViewModel) and when locally is saved I could just add as Goals.Add(NewGoal)? Should it be part of the BaseViewModel?