ViewModels in MVVM
ViewModels are a core component of the Model-View-ViewModel (MVVM) architectural pattern in .NET MAUI. They act as intermediaries between the View and the Model, exposing data and commands that the View can bind to.
Purpose of ViewModels
ViewModels are responsible for:
- Exposing Data: They hold the data that the View needs to display. This data is typically retrieved from the Model or other services.
- Handling User Input: They process user interactions initiated through the View, often by executing Commands.
- Maintaining State: They manage the state of the View, ensuring that data is up-to-date and consistent.
- Decoupling: They decouple the View from the Model, making the application more testable and maintainable. The View knows nothing about the Model, and the Model knows nothing about the View.
Creating a ViewModel
ViewModels are typically implemented as plain C# classes. They often inherit from ObservableObject
(provided by the Community Toolkit.MVVM) or implement INotifyPropertyChanged
to notify the View when properties change.
Example: A Simple ViewModel
Let's create a basic ViewModel for a counter feature:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Windows.Input;
public partial class CounterViewModel : ObservableObject
{
[ObservableProperty]
private int _count;
public ICommand IncrementCommand { get; }
public CounterViewModel()
{
_count = 0;
IncrementCommand = new RelayCommand(IncrementCount);
}
private void IncrementCount()
{
Count++;
}
}
[ObservableProperty]
attribute from the Community Toolkit.MVVM simplifies property notification by automatically generating the property and its backing field with notification logic.
Data Binding
The View binds to properties and commands exposed by the ViewModel. When a property in the ViewModel changes, the View automatically updates to reflect the new value. Similarly, when a user interacts with a UI element bound to a Command, the ViewModel's corresponding method is executed.
Binding to Properties
In XAML, you can bind to a ViewModel property like this:
<Label Text="{Binding Count}" />
Binding to Commands
And bind a button to execute a ViewModel command:
<Button Command="{Binding IncrementCommand}" Text="Increment" />
ViewModel Lifecycle and Management
How you manage and instantiate your ViewModels is crucial. Common approaches include:
- Dependency Injection: Registering ViewModels with a DI container and resolving them in your View or application setup. This is the recommended approach for managing dependencies and lifecycles.
- Service Locators: A simpler but less flexible alternative to DI.
- Direct Instantiation: For very simple scenarios, you might instantiate ViewModels directly, but this can lead to tight coupling.
Common ViewModel Patterns
Displaying Lists
ViewModels often expose collections of data that can be displayed in UI elements like ListView
or CollectionView
. The collection itself should typically implement INotifyCollectionChanged
(e.g., ObservableCollection<T>
) to notify the View when items are added, removed, or changed.
Handling Asynchronous Operations
For operations that take time (e.g., network requests, database queries), ViewModels should use asynchronous programming patterns (async/await
) and often expose properties to indicate loading states.
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private ObservableCollection<string> _items;
public ICommand LoadItemsCommand { get; }
public MyViewModel()
{
Items = new ObservableCollection<string>();
LoadItemsCommand = new AsyncRelayCommand(LoadDataAsync);
}
private async Task LoadDataAsync()
{
IsLoading = true;
try
{
// Simulate fetching data
await Task.Delay(2000);
Items.Add("Item 1");
Items.Add("Item 2");
Items.Add("Item 3");
}
finally
{
IsLoading = false;
}
}
Next Steps
Now that you understand ViewModels, explore how Commands facilitate user interaction and how Messengers enable communication between ViewModels.