.Net MAUI - MVVM Pattern for Cleaner Architecture
Applying the MVVM Pattern for Cleaner Architecture in .NET MAUI
Building cross-platform apps in .NET MAUI is exciting — but if your code isn’t organized well, you’ll quickly end up with tangled spaghetti. That’s where MVVM (Model-View-ViewModel) comes to the rescue.
Join our exclusive WhatsApp group for Xamarin and .NET MAUI developers to connect with experts, share insights, and get help with your projects. Whether you're a beginner or an experienced developer, this group is the perfect place to enhance your skills and collaborate with the community.
Please, support my blog by clicking on our sponsors ad!
In this blog, we’ll:
- Cover both basic and advanced MVVM implementation
- Write and explain a reusable BaseViewModel
- Learn how to use async APIs, loading indicators, and commands
- Help you prepare for interviews with a curated question list at the end
🎯 At the end of the blog, you'll find MVVM interview questions to help you prepare for job interviews. Comment your answers below to engage and learn from others.
🧠 What is MVVM?
MVVM is a design pattern used to separate concerns in your app architecture.
- Model: The data or business logic layer (e.g., data models, API calls).
- View: The UI (written in XAML).
- ViewModel: The mediator between Model and View — handles logic and exposes properties/commands for data binding.
This structure helps achieve:
- Better testability
- Clean separation of concerns
- Reusable logic
- Simplified UI updates
🛠 MVVM in .NET MAUI – Step-by-Step Implementation
🧱 Basic MVVM Example – TODO App
Let’s create a simple TODO app using MVVM to get started.
📁 Folder Structure
MyTodoApp
├── Models
│ └── TodoItem.cs
├── ViewModels
│ ├── BaseViewModel.cs
│ └── TodoViewModel.cs
├── Views
│ └── MainPage.xaml
│ └── MainPage.xaml.cs
🧩 Step 1: Model
// Models/TodoItem.cs
public class TodoItem
{
public string Title { get; set; }
public bool IsCompleted { get; set; }
}
📍 Step 2: BaseViewModel (💡 Reusability Magic)
Why do we need BaseViewModel?
ViewModels need to:
- Notify the UI when properties change (using INotifyPropertyChanged)
- Share common states like IsBusy, Title, or ErrorMessage
Instead of repeating this code in every ViewModel, we extract it into a BaseViewModel.
// ViewModels/BaseViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
bool _isBusy;
public bool IsBusy
{
get => _isBusy;
set => SetProperty(ref _isBusy, value);
}
string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
🧠 What does it do?
- INotifyPropertyChanged: Notifies the UI about property updates.
- SetProperty(): A helper to avoid repeating if (value != _x)... boilerplate.
- IsBusy: Useful for showing loaders/spinners.
- Title: Can be used for binding page titles dynamically.
Table of Contents for .Net Maui
- What is .NET MAUI and why it’s important
- Applying the MVVM pattern for cleaner architecture
- Working with Renderers and Mappers
- Storing data locally using SQLite and Preferences
- Image Picker from gallery
- Sending push notifications using Firebase
- Publishing your app to Android and iOS stores
- 🌟 Explore More Topics
Every ViewModel you write can now inherit from this class and get this functionality for free.
🧠 Step 3: ViewModel – TodoViewModel
// ViewModels/TodoViewModel.cs
using System.Collections.ObjectModel;
using System.Windows.Input;
public class TodoViewModel : BaseViewModel
{
private string _newTask;
public string NewTask
{
get => _newTask;
set => SetProperty(ref _newTask, value);
}
public ObservableCollection<TodoItem> Items { get; set; } = new();
public ICommand AddTaskCommand { get; }
public TodoViewModel()
{
Title = "My Todo List";
AddTaskCommand = new Command(AddTask);
}
private void AddTask()
{
if (!string.IsNullOrWhiteSpace(NewTask))
{
Items.Add(new TodoItem { Title = NewTask, IsCompleted = false });
NewTask = string.Empty;
}
}
}
🖼 Step 4: View – MainPage.xaml
<!-- Views/MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyTodoApp.ViewModels"
x:Class="MyTodoApp.Views.MainPage">
<ContentPage.BindingContext>
<vm:TodoViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout Padding="20">
<Label Text="{Binding Title}" FontSize="24" HorizontalOptions="Center" />
<Entry Placeholder="Enter task"
Text="{Binding NewTask}" />
<Button Text="Add Task"
Command="{Binding AddTaskCommand}" />
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10">
<Label Text="{Binding Title}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
🚀 Advanced MVVM Features
Now let’s simulate:
- Loading data from an API
- Showing a loading spinner
- Using async/await
🧪 Simulated API Fetch
public class TodoViewModel : BaseViewModel
{
public ObservableCollection<TodoItem> Items { get; set; } = new();
public ICommand LoadTasksCommand { get; }
public TodoViewModel()
{
LoadTasksCommand = new Command(async () => await LoadTasksAsync());
}
private async Task LoadTasksAsync()
{
if (IsBusy) return;
try
{
IsBusy = true;
await Task.Delay(2000); // Simulate network delay
Items.Clear();
Items.Add(new TodoItem { Title = "Learn MVVM", IsCompleted = false });
Items.Add(new TodoItem { Title = "Practice MAUI", IsCompleted = true });
}
finally
{
IsBusy = false;
}
}
}
🔄 Spinner and Load Button in View
<Button Text="Load Tasks"
Command="{Binding LoadTasksCommand}" />
<ActivityIndicator IsRunning="{Binding IsBusy}"
IsVisible="{Binding IsBusy}"
VerticalOptions="Center" />
🧠 MVVM Best Practices
- Use a BaseViewModel for shared logic.
- Avoid code-behind in your views. Keep logic in ViewModel.
- Use Commands instead of event handlers.
- Group logic in Services (for APIs, databases) and inject them into ViewModels.
- Keep your ViewModel unit-testable — no references to UI controls.
💡 Conclusion
MVVM is powerful, and it’s an essential skill for modern mobile app development. With .NET MAUI, MVVM is easier than ever to implement, especially with reusable BaseViewModels and simple data binding.
📝 Interview Questions
- What is MVVM, and why is it useful in mobile app development?
- How does data binding work in MVVM?
- Explain the role of the ViewModel in the MVVM pattern.
- What is the difference between MVVM and MVC?
- How would you handle complex state management in a large MVVM application?
We hope this blog has been helpful! If you have any questions or answers to the interview questions, feel free to comment below.
Comments
Post a Comment