The usage of the MVVM pattern in .NET MAUI implies implementing the INotifyPropertyChanged interface in every ViewModel class. Not to repeat similar code, it’s better to create a BaseViewModel class that implements common functionality and allow all other ViewModels to inherit from it.
Create the BaseViewModel Class
This class will implement the INotifyPropertyChanged and implement a reusable method to notify property changes.
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MauiAppExample.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
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;
}
}
}
INotifyPropertyChanged: This provides an interface to notify the view about property changes.
OnPropertyChanged: This method is used to raise the PropertyChanged event.
SetProperty: A helper method that sets the value of a property and raises the PropertyChanged event if the value changes.
Create a ViewModel Class Inheriting BaseViewModel
Let’s create a ProductViewModel that inherits from BaseViewModel.
using System.Collections.ObjectModel;
using System.Windows.Input;
using MauiAppExample.Models;
namespace MauiAppExample.ViewModels
{
public class ProductViewModel : BaseViewModel
{
private string newProductName;
public string NewProductName
{
get => newProductName;
set => SetProperty(ref newProductName, value);
}
public ObservableCollection<Product> Products { get; } = new ObservableCollection<Product>();
public ICommand AddProductCommand { get; }
public ProductViewModel()
{
AddProductCommand = new Command(OnAddProduct);
}
private void OnAddProduct()
{
if (!string.IsNullOrWhiteSpace(NewProductName))
{
Products.Add(new Product { Name = NewProductName });
NewProductName = string.Empty; // Reset the input field after adding the product
}
}
}
}
NewProductName: Input field bound property on the add new-product page.
Products: A list to hold the assortment of products.
AddProductCommand: Command to add a new product to the list.
SetProperty: The BaseViewModel’s helper method is used to set the property value and notify changes.
Create the Product Model Class
Let’s create a Product model to store information about each product.
namespace MauiAppExample.Models
{
public class Product
{
public string Name { get; set; }
}
}
Create the View (MainPage)
Let’s create a view that will use ProductViewModel.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiAppExample.ViewModels"
x:Class="MauiAppExample.MainPage">
<ContentPage.BindingContext>
<local:ProductViewModel />
</ContentPage.BindingContext>
<StackLayout Padding="20" Spacing="15">
<!-- Entry for Product Name -->
<Entry Placeholder="Enter product name"
Text="{Binding NewProductName}"
FontSize="18" />
<!-- Button to Add Product -->
<Button Text="Add Product"
Command="{Binding AddProductCommand}"
FontSize="18" />
<!-- ListView to Display Products -->
<ListView ItemsSource="{Binding Products}"
Margin="0,20,0,0">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Advantages of Using BaseViewModel
- Code Reuse: In an attempt to reduce duplication, the common INotifyPropertyChanged logic is shared between different ViewModels.
- ViewModels Simplified: Each of them would contain only its properties and its respective logic, making the code easier to maintain.
- Centralized Change Notification: Each ViewModel defines the pattern for change notification in the same way, thus it’s centralized in the app.
This will help you clean up and reduce the boilerplate code in your MVVM implementation. The BaseViewModel can be extended further for other common features.