The MVVM pattern separates the interface (View) from presentation logic (ViewModel). The CommunityToolkit.Mvvm package removes almost all the boilerplate thanks to source generators.
A complete ViewModel
public partial class ProfileViewModel(IProfileService service) : ObservableObject
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SaveCommand))]
public partial string Name { get; set; } = "";
[ObservableProperty]
public partial bool IsSaving { get; set; }
private bool CanSave() => !string.IsNullOrWhiteSpace(Name) && !IsSaving;
[RelayCommand(CanExecute = nameof(CanSave))]
private async Task SaveAsync()
{
IsSaving = true;
try { await service.SaveAsync(Name); }
finally { IsSaving = false; }
}
}
The generator creates the property change notifications and the SaveCommand command, which enables and disables itself based on CanSave.
The View
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:DataType="vm:ProfileViewModel">
<VerticalStackLayout Padding="20" Spacing="12">
<Entry Text="{Binding Name}" Placeholder="Name" />
<Button Text="Save" Command="{Binding SaveCommand}" />
<ActivityIndicator IsRunning="{Binding IsSaving}" />
</VerticalStackLayout>
</ContentPage>
x:DataType enables compiled bindings: checked at compile time and faster at runtime.
Registration
builder.Services.AddSingleton<IProfileService, ProfileService>();
builder.Services.AddTransient<ProfileViewModel>();
builder.Services.AddTransient<ProfilePage>();
The page receives the ViewModel in its constructor and assigns it to the BindingContext. To let different ViewModels communicate without direct references, use WeakReferenceMessenger, which replaces the old MessagingCenter.
Comments (0)
No comments yet.