MAUI-CommunityToolkit-6 :👈 👉:MAUI-CommunityToolkit-8

Hour 7 – Navigation, Messaging & Dependency Injection in MVVM

πŸ“˜ Hour 7 – Navigation, Messaging & Dependency Injection (DI) in MVVM

Welcome to Hour 7 of your MAUI MVVM learning series. Today we focus on three essential architecture concepts used in modern MAUI apps:

  • ➑ Navigation (Shell navigation)
  • ➑ Messaging (weak messaging between ViewModels)
  • ➑ Dependency Injection (services with constructor injection)

✨ 1. Navigation in .NET MAUI (Shell)

In MAUI, the recommended way of navigating pages is the Shell Navigation system. It is simple, URL‑based, and works perfectly with MVVM.

πŸ“„ 1.1 Registering Routes

Add this inside AppShell.xaml.cs:

Routing.RegisterRoute("details", typeof(DetailsPage));

➑ 1.2 Navigating from ViewModel

Inject Shell.Current navigation into ViewModel using Dependency Injection.

[RelayCommand]
private async Task GoToDetails()
{
    await Shell.Current.GoToAsync("details");
}

➑ 1.3 Navigating With Parameters

await Shell.Current.GoToAsync("details?itemId=5&title=Hello");

Receiving parameters in destination ViewModel:

[QueryProperty(nameof(ItemId), "itemId")]
[QueryProperty(nameof(Title), "title")]
public partial class DetailsViewModel : ObservableObject
{
    [ObservableProperty]
    private int itemId;

    [ObservableProperty]
    private string title;
}

βœ” Parameters are automatically assigned when the page loads.


✨ 2. Messaging in MVVM (WeakReferenceMessenger)

Messaging is useful when two ViewModels need to communicate without knowing each other. Example: Refreshing a list when another page updates data.

πŸ“€ 2.1 Sending a Message

WeakReferenceMessenger.Default.Send(new ItemUpdatedMessage(42));

πŸ“₯ 2.2 Receiving a Message

WeakReferenceMessenger.Default.Register<ItemUpdatedMessage>(this, (r, m) =>
{
    Console.WriteLine("Item updated ID: " + m.Value);
});

🧩 2.3 Message Definition

public class ItemUpdatedMessage : ValueChangedMessage<int>
{
    public ItemUpdatedMessage(int value) : base(value)
    {
    }
}

βœ” Decoupled. βœ” Automatic cleanup. βœ” Ideal for cross-page communication.


✨ 3. Dependency Injection (DI)

.NET MAUI uses built‑in DI. You register your app's services in MauiProgram.cs.

πŸ”§ 3.1 Registering Services

builder.Services.AddSingleton<IDataService, DataService>();
builder.Services.AddTransient<MainViewModel>();
builder.Services.AddTransient<MainPage>();

βœ” Singleton β†’ one instance for entire app βœ” Transient β†’ new instance each time a page loads

➑ 3.2 Injecting Services Into ViewModel

public partial class MainViewModel : ObservableObject
{
    private readonly IDataService dataService;

    public MainViewModel(IDataService service)
    {
        dataService = service;
    }
}

βœ” Constructor injection, no service locators needed.


✨ 4. Complete Example (Navigation + Messaging + DI)

πŸ“„ 4.1 Register Services

builder.Services.AddSingleton<ItemService>();
builder.Services.AddTransient<ListViewModel>();
builder.Services.AddTransient<ListPage>();
builder.Services.AddTransient<EditViewModel>();
builder.Services.AddTransient<EditPage>();

πŸ“„ 4.2 ListViewModel

public partial class ListViewModel : ObservableObject
{
    private readonly ItemService items;

    public ListViewModel(ItemService service)
    {
        items = service;

        WeakReferenceMessenger.Default.Register<ItemUpdatedMessage>(this, (r, m) =>
        {
            LoadItemsCommand.Execute(null);
        });
    }

    [RelayCommand]
    private async Task LoadItems()
    {
        Items = items.GetAll();
    }

    [ObservableProperty]
    private List<string> items;
}

πŸ“„ 4.3 EditViewModel

public partial class EditViewModel : ObservableObject
{
    [ObservableProperty]
    private int itemId;

    [ObservableProperty]
    private string title;

    [RelayCommand]
    private async Task Save()
    {
        WeakReferenceMessenger.Default.Send(new ItemUpdatedMessage(ItemId));
        await Shell.Current.GoToAsync("..");
    }
}

βœ” Flow:

  • ➑ Edit page saves item
  • ➑ Sends message to list page
  • ➑ List page refreshes
  • ➑ Navigation returns

πŸ“˜ Hour 7 Summary

  • βœ” Navigation uses Shell and URL-style routing
  • βœ” Parameters can be passed via QueryProperty
  • βœ” Messaging enables decoupled ViewModel communication
  • βœ” DI provides clean and testable architecture
  • βœ” Combined techniques allow scalable, professional MAUI apps

πŸ“… Hour 7 – Final Mini Project & MVVM Toolkit Best Practices

πŸš€ 1. Goal of Hour 7

The Hour 7 focuses on building a complete small app using everything you learned: ObservableProperty, Commands, AsyncRelayCommand, Messenger, DI, Navigation, and Best Practices.

You will:

  • 🧱 Design a proper folder structure
  • πŸŽ›οΈ Implement ViewModels using MVVM Toolkit
  • πŸ“‘ Add messaging between pages
  • πŸ“¨ Pass navigation parameters
  • πŸ”§ Use services through Dependency Injection
  • ✨ Apply MVVM best practices

πŸ“˜ 2. Suggested Mini Projects (Choose One)

Option A – Todo App

  • Add / Update / Delete tasks
  • Use Messenger for refresh events
  • Use DI for a TaskService
  • Navigate to Detail page to edit task

Option B – Product Inventory App

  • List products
  • Edit product quantity
  • Use ValueChangedMessage to notify updates
  • Use DI for ProductService

Option C – Notes App

  • Add and view notes
  • Use AsyncRelayCommand for loading notes
  • Navigate to NoteDetailViewModel with parameters

πŸ“ 3. Recommended Project Structure

πŸ“‚ ProjectName
 ┣ πŸ“ Views
 ┣ πŸ“ ViewModels
 ┣ πŸ“ Models
 ┣ πŸ“ Services
 ┣ πŸ“ Messages
 β”— App.xaml / MauiProgram.cs

βœ” Clean separation of responsibility


πŸ”§ 4. Example Service

ITodoService

public interface ITodoService
{
    Task<List<TodoItem>> GetTodosAsync();
    Task AddAsync(TodoItem item);
}

Implementation:

public class TodoService : ITodoService
{
    private readonly List<TodoItem> items = new();

    public Task<List<TodoItem>> GetTodosAsync() => Task.FromResult(items);

    public Task AddAsync(TodoItem item)
    {
        items.Add(item);
        return Task.CompletedTask;
    }
}

🧩 5. Example Message (for Refresh)

public class TodoAddedMessage : ValueChangedMessage<bool>
{
    public TodoAddedMessage(bool value) : base(value) { }
}

βœ” When a new task is added, other screens refresh.


πŸŽ›οΈ 6. ViewModel Example (Main List Screen)

public partial class HomeViewModel : ObservableObject
{
    private readonly ITodoService service;

    [ObservableProperty]
    private List<TodoItem> items;

    public HomeViewModel(ITodoService todoService)
    {
        service = todoService;

        WeakReferenceMessenger.Default.Register<TodoAddedMessage>(this, (r, m) =>
        {
            LoadCommand.Execute(null);
        });
    }

    [ICommand]
    private async Task LoadAsync()
    {
        Items = await service.GetTodosAsync();
    }

    [ICommand]
    private async Task AddNewAsync()
    {
        await Shell.Current.GoToAsync("addtodo");
    }
}

βœ” Uses AsyncRelayCommand βœ” Uses Messenger for refresh βœ” Uses DI βœ” Uses Navigation βœ” Uses ObservableProperty βœ” Clean MVVM


πŸ“ 7. AddTodoViewModel (Adding a New Task)

public partial class AddTodoViewModel : ObservableObject
{
    private readonly ITodoService service;

    public AddTodoViewModel(ITodoService todoService)
    {
        service = todoService;
    }

    [ObservableProperty]
    private string title;

    [ICommand]
    private async Task SaveAsync()
    {
        await service.AddAsync(new TodoItem { Title = Title });

        WeakReferenceMessenger.Default.Send(new TodoAddedMessage(true));

        await Shell.Current.GoToAsync("..");
    }
}

βœ” Sends message βœ” Saves item βœ” Navigates back


πŸ“„ 8. Best Practices for MVVM Toolkit

  • πŸ”Ή Use [ObservableProperty] for all bindable fields
  • πŸ”Ή Use [ICommand] instead of manual RelayCommand creation
  • πŸ”Ή Use AsyncRelayCommand for async operations
  • πŸ”Ή Use WeakReferenceMessenger for ViewModel communication
  • πŸ”Ή Don’t inject pages into ViewModels (only services)
  • πŸ”Ή Use DI to provide services to ViewModels
  • πŸ”Ή Keep ViewModels thin β€” business logic goes into services
  • πŸ”Ή Keep Views dumb β€” no code-behind except for UI-specific actions
  • πŸ”Ή Bring all navigation inside ViewModels
  • πŸ”Ή Use partial methods for validation (OnXChanging, OnXChanged)

πŸ› οΈ 9. Hour 7 Hands-On Tasks

  • ✨ Build one complete mini-app
  • πŸ“¨ Include Messenger communication
  • πŸ“ Use proper folder structure
  • 🧱 Create at least 2 ViewModels
  • πŸ”§ Add 1 Service with DI
  • ➑️ Use Shell navigation with parameters
  • 🎯 Use [ICommand] for all commands
  • πŸ”— Use [NotifyPropertyChangedFor] for dependent properties
  • πŸ§ͺ Add at least one validation using partial methods

🏁 Hour 7 Final Outcome

You now have:

  • 🎯 Full MVVM Toolkit understanding
  • 🧱 Ability to build real apps using MVVM Toolkit
  • 🧩 Deep knowledge of source generators
  • πŸ“‘ Expertise in Messenger communication
  • πŸ”§ DI skills for scalable architecture
  • ⚑ Ability to structure professional-level MAUI/WPF apps

πŸ“ Hour 7 – Final Project & Best Practices Quiz

πŸ“˜ Section A – Multiple Choice Questions

  1. What is the main purpose of applying MVVM best practices?
    a) To reduce app size
    b) To avoid using XAML
    c) To create clean, maintainable, testable applications
    d) To remove async methods
  2. Where should business logic ideally be placed in MVVM apps?
    a) Code-behind
    b) Services
    c) Views
    d) Models
  3. What should ViewModels avoid referencing?
    a) Models
    b) UI Controls (Pages, Buttons, Views)
    c) Services
    d) Data classes
  4. Which tool is best suited for notifying other ViewModels about an update?
    a) Events
    b) Database triggers
    c) WeakReferenceMessenger
    d) Static global variables
  5. Which of the following is the cleanest folder structure for MVVM?
    a) One folder with everything
    b) Splitting files by developer name
    c) Views, ViewModels, Models, Services, Messages
    d) Random folders created during development

🧠 Section B – True or False

  1. True / False: Navigation should be done inside ViewModels, not in the Views.
  2. True / False: A Service should never be injected into a ViewModel.
  3. True / False: Using Messenger helps reduce tight coupling between screens.
  4. True / False: Partial methods (OnXChanged) are useful for validation and side effects.
  5. True / False: A mini project must use AsyncRelayCommand for all async operations.

✍️ Section C – Short Answer Questions

  1. Why is Dependency Injection essential in a scalable MVVM project?
  2. Explain why Views should contain minimal or no logic in MVVM.
  3. What is the purpose of using separate folders for ViewModels, Views, and Services?
  4. Give an example scenario where Messenger is better than direct ViewModel reference.
  5. What does Shell navigation offer that normal navigation methods do not?

πŸ’» Section D – Code Interpretation

Given:

public partial class HomeViewModel : ObservableObject
{
    private readonly ITaskService taskService;

    [ObservableProperty]
    private List<TaskItem> tasks;

    public HomeViewModel(ITaskService service)
    {
        taskService = service;

        WeakReferenceMessenger.Default.Register<TaskAddedMessage>(this, (r, m) =>
        {
            LoadCommand.Execute(null);
        });
    }

    [ICommand]
    private async Task LoadAsync()
    {
        Tasks = await taskService.GetTasksAsync();
    }
}
  1. What message is HomeViewModel listening for?
  2. What does LoadCommand do?
  3. Why is DI used for ITaskService?
  4. What happens when TaskAddedMessage is sent?

πŸ“¦ Section E – Architecture & Best Practices

Given this folder structure:

πŸ“‚ Project
 ┣ πŸ“ Views
 ┣ πŸ“ ViewModels
 ┣ πŸ“ Models
 ┣ πŸ“ Services
 β”— πŸ“ Helpers
  1. Where should message classes be placed?
  2. Where should a Login API call be implemented?
  3. Where should validation logic for Username be placed using partial methods?

πŸ“˜ Hour 7 – Final Project & Best Practices Quiz Answer Key

βœ… Section A – Multiple Choice Answers

  1. c) To create clean, maintainable, testable applications
  2. b) Services
  3. b) UI Controls (Pages, Buttons, Views)
  4. c) WeakReferenceMessenger
  5. c) Views, ViewModels, Models, Services, Messages

🧠 Section B – True or False Answers

  1. True β€” Navigation logic should live inside ViewModels in MVVM.
  2. False β€” Services SHOULD be injected into ViewModels using DI.
  3. True β€” Messenger enables decoupled communication.
  4. True β€” Partial methods can run validation or side effects.
  5. False β€” Only async operations require AsyncRelayCommand.

✍️ Section C – Short Answer Solutions

  1. DI keeps code loosely coupled, makes services reusable, and ensures ViewModels are easy to test by injecting dependencies.
  2. Views should only render UI. Logic in Views creates tight coupling and breaks MVVM.
  3. Folder separation keeps the project organized and scalable. Each layer has a single responsibility.
  4. When one ViewModel updates data (e.g., adding a task) and another ViewModel must refresh the list β€” Messenger solves this without direct references.
  5. Shell navigation supports parameter passing, deep linking, routing, and cleaner navigation URIs (e.g., "details?id=5").

πŸ’» Section D – Code Interpretation Answers

  1. TaskAddedMessage
  2. LoadCommand triggers LoadAsync(), which loads all tasks from the service.
  3. DI allows using different service implementations and makes the ViewModel testable and loosely coupled.
  4. HomeViewModel calls LoadCommand.Execute(null), refreshing the tasks list on screen.

πŸ“¦ Section E – Architecture & Best Practices Answers

  1. Message classes should go into a folder named Messages.
  2. A Login API call should be implemented in Services.
  3. Username validation using partial methods belongs in the ViewModel as OnUsernameChanging or OnUsernameChanged.

πŸŽ‰ Hour 7 Answer Key Complete

Your Hour 7‑ journey with MVVM Toolkit is completed!

Back to Index
MAUI-CommunityToolkit-6 :👈 👉:MAUI-CommunityToolkit-8
*