MVVM Pattern in .NET MAUI
Intermediate
30 min read
Lesson 6 of 8
What is MVVM?
MVVM (Model-View-ViewModel) is an architectural pattern that separates the user interface (View) from the business logic (ViewModel) and data (Model). It enables better separation of concerns, testability, and maintainability.
MVVM Components
- Model - Data and business logic
- View - UI and user interaction
- ViewModel - Bridge between Model and View
MVVM Architecture
// Project Structure
MyApp/
├── Models/
│ └── (Data models)
├── ViewModels/
│ └── (View models)
├── Views/
│ └── (XAML pages)
└── Services/
└── (Data services)
Implementing MVVM
1. The Model
// Models/Product.cs
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public string Category { get; set; }
public DateTime CreatedDate { get; set; }
}
2. The ViewModel
// ViewModels/ProductViewModel.cs
using System.Collections.ObjectModel;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class ProductViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection products;
[ObservableProperty]
private Product selectedProduct;
[ObservableProperty]
private string searchText;
[ObservableProperty]
private bool isLoading;
private readonly IProductService productService;
public ProductViewModel(IProductService productService)
{
this.productService = productService;
Products = new ObservableCollection();
LoadProductsCommand = new AsyncRelayCommand(LoadProductsAsync);
SaveProductCommand = new AsyncRelayCommand(SaveProductAsync);
DeleteProductCommand = new AsyncRelayCommand(DeleteProductAsync);
}
public IAsyncRelayCommand LoadProductsCommand { get; }
public IAsyncRelayCommand SaveProductCommand { get; }
public IAsyncRelayCommand DeleteProductCommand { get; }
private async Task LoadProductsAsync()
{
try
{
IsLoading = true;
var items = await productService.GetProductsAsync();
Products.Clear();
foreach (var item in items)
Products.Add(item);
}
finally
{
IsLoading = false;
}
}
private async Task SaveProductAsync()
{
if (SelectedProduct == null) return;
await productService.SaveProductAsync(SelectedProduct);
await LoadProductsAsync();
}
private async Task DeleteProductAsync()
{
if (SelectedProduct == null) return;
await productService.DeleteProductAsync(SelectedProduct.Id);
await LoadProductsAsync();
}
}
3. The View
// Views/ProductsPage.xaml
Data Binding
// One-way binding
// Two-way binding
// One-way to source
// One-time binding
// Default binding mode
Command Binding
// ViewModel
public ICommand SaveCommand { get; }
public ProductViewModel()
{
SaveCommand = new RelayCommand(Save, CanSave);
}
private bool CanSave()
{
return !string.IsNullOrEmpty(SelectedProduct?.Name);
}
private void Save()
{
// Save logic
}
// View
Dependency Injection with MVVM
// Services registration in MauiProgram.cs
builder.Services.AddSingleton();
builder.Services.AddSingleton();
// ViewModel injection
public partial class ProductsPage : ContentPage
{
public ProductsPage(ProductViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}
// Using Service Provider
var viewModel = ServiceHelper.GetService();
Key Takeaway
- MVVM separates UI from business logic
- Data binding connects View and ViewModel
- Commands handle user actions
- Dependency injection improves testability
Exercise
Implement MVVM for a simple app:
- Create Model for your data
- Implement ViewModel with properties and commands
- Create View with XAML
- Implement data binding
- Add CRUD operations
- Implement validation