How to reuse globally already loaded observable collection in Maui app?

I have a Maui app where 4-5 observable collections are used across the application on different views. Currently I have a service which loads from database via repository and trigger an event to update the observable collection but this needs to be loaded every single pages where this data is used.

ToDoService:

public class ToDoService
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly List<ToDo> _toDos;
    public IEnumerable<ToDo> ToDos => _toDos;

    public event Action ToDosLoaded;
    public event Action<ToDo> ToDoAdded;
    public event Action<ToDo> ToDoUpdated;
    public event Action<Guid> ToDoDeleted;

    public ToDoService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        _toDos = new List<ToDo>();
    }

    public async Task Load()
    {
        IEnumerable<ToDo> toDos = await _unitOfWork.ToDo.GetAllAsync();
        _toDos.Clear();
        _toDos.AddRange(toDos);

        ToDosLoaded?.Invoke();
    }

    public async Task Add(ToDo toDo, bool isCopy = false)
    {
        var saved = await _unitOfWork.ToDo.SaveAsync(toDo);

        if (saved is not null)
        {
            _toDos.Add(toDo);

            ToDoAdded?.Invoke(toDo);
        }
    }

    public async Task Update(ToDo toDo)
    {
        var saved = await _unitOfWork.ToDo.SaveAsync(toDo);

        if (saved is not null)
        {
            int currentIndex = _toDos.FindIndex(y => y.LocalId == saved.LocalId);

            if (currentIndex != -1)
            {
                _toDos[currentIndex] = saved;
            }
            else
            {
                _toDos.Add(saved);
            }

            ToDoUpdated?.Invoke(saved);
        }
    }

    public async Task Delete(ToDo toDo)
    {
        var deleted = await _unitOfWork.ToDO.DeleteAsync(toDo.LocalId);

        if (deleted)
        {
            _toDos.RemoveAll(g => g.LocalId == toDo.LocalId);

            ToDoDeleted?.Invoke(toDo.LocalId);
        }
    }
}

ToDoListingViewModel:

private readonly ToDoService _toDoStoreService;

[ObservableProperty]
private ObservableCollection<ToDoListingPreviewItemViewModel> _toDoListingPreviewItemViewModels;

public ToDoListingViewModel(ToDoService toDoStoreService)
{
    ToDoListingPreviewItemViewModels = [];

    _toDoStoreService = toDoStoreService;
    _toDoStoreService.ToDosLoaded += ToDoStoreService_ToDosLoaded;
    _toDoStoreService.ToDoAdded += ToDoStoreService_ToDoAdded;
    _toDoStoreService.ToDoUpdated += ToDoStoreService_ToDoUpdated;
    _toDoStoreService.ToDoDeleted += ToDoStoreService_ToDoDeleted;

    ToDoListingPreviewItemViewModels.CollectionChanged += ToDoListingItemViewModels_CollectionChanged;
}

private void ToDoStoreService_ToDosLoaded()
{
    ToDoListingPreviewItemViewModels.Clear();

    foreach (ToDo toDo in _toDoStoreService.ToDos)
    {
        AddToDo(toDo);
    }
}

private void ToDoStoreService_ToDoAdded(ToDo toDo)
{
    AddToDo(toDo);
}

private void ToDoStoreService_ToDoUpdated(ToDo toDo)
{
    ToDoListingPreviewItemViewModel toDoListingPreviewItemViewModel =
            ToDoListingPreviewItemViewModels.FirstOrDefault(t => t.ToDo.LocalId == toDo.LocalId);

    if (toDoListingPreviewItemViewModel != null)
    {
        toDoListingPreviewItemViewModel.Update(toDo);
    }
}

private void ToDoStoreService_ToDoDeleted(Guid id)
{
    ToDoListingPreviewItemViewModel toDoListingPreviewItemViewModel = ToDoListingPreviewItemViewModels.FirstOrDefault(y => y.ToDo?.LocalId == id);

    if (toDoListingPreviewItemViewModel != null)
    {
        ToDoListingPreviewItemViewModels.Remove(toDoListingPreviewItemViewModel);
    }
}

private void ToDoListingItemViewModels_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    OnPropertyChanged(nameof(Name));
}

private void AddToDo(ToDo toDo)
{
    ToDoListingPreviewItemViewModel toDoListingPreviewItemViewModel = new(toDo);
    ToDoListingPreviewItemViewModels.Add(toDoListingPreviewItemViewModel);
}

public void Dispose()
{
    _toDoStoreService.ToDosLoaded -= ToDoStoreService_ToDosLoaded;
    _toDoStoreService.ToDoAdded -= ToDoStoreService_ToDoAdded;
    _toDoStoreService.ToDoUpdated -= ToDoStoreService_ToDoUpdated;
    _toDoStoreService.ToDoDeleted -= ToDoStoreService_ToDoDeleted;

    ToDoListingPreviewItemViewModels.CollectionChanged -= ToDoListingItemViewModels_CollectionChanged;
}

With this way I can simply create

public ToDosListingViewModel ToDos { get; }
ToDos = new ToDosListingViewModel (_todosStoreService);
// and load data
await _todosStoreService.Load();

With this setup if any page needs to use this collection I need to load all first and if anywhere a change happens it will trigger all lists which have been loaded before to update all of them across the application. It is great but I would like to avoid to load multiple times and subscribing for the events to update. How can I use one list property across the application?

Shall I just make ToDosListingViewModel and ToDoService singleton and when the app starts i just load all data once and thats all?
Shall I add a global list variable to the App.cs?

5

One way to do this is to create and register a GlobalViewModel. Note that it is enough to declare ObservableCollection with a getter and use an initializer or constructor to initialize it. There’s no need to use the [ObservableProperty] attribute.

public class GlobalViewModel
{
     public ObservableCollection<ToDoListingPreviewItemViewModel> ToDoListingPreviewItemViewModels {get;} = new;
}

Then, you register this GlobalViewModal as a singleton:

    // MauiProgram.cs
    builder.Services.AddSingleton<GlobalViewModel>();
    builder.Services.AddTransient<MainPage>();

Then, for all pages that need access to it, you can use dependency injection and gain a copy of it:

// MainPage.xaml.cs
public class MainPage
{
    public GlobalViewModel GVM { get;}

    public MainPage(GlobalViewModel gvm)
    {
        this.GVM = gvm;
        InitializeComponent();
        BindingContext = this;
    }
}

And, in your XAML, you can access the collection with GVM.ToDoListingPreviewItemViewModels

For this, you can create a static global class for your app. And you can access and update these parameters of this static global class across your app.

For example, we can first define a global class (AppData.cs) and add a variable Items_test:

public static class AppData
{
    public static ObservableCollection<string> Items_test { get; set; } = new ObservableCollection<string>() {"test1", "test2" };
}

Then we can access variables of type ObservableCollection<> on any page of your app.

For example:

 AppData.Items_test.Add("test3");

Of course, we can also define the global static data into App.xaml.cs. And we can access this data by the following code:

  App.Items_test2.Add("test");

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật