# Source generators reference Complete attribute reference for `CommunityToolkit.Mvvm` 8.x source generators, with the code each one produces. > **Universal rule.** Every type that uses one of these attributes — and > every enclosing type, if nested — must be declared `partial`. The > generators emit a sibling partial class declaration; without `partial`, > the compiler reports `MVVMTK0008` / `MVVMTK0042`. --- ## `[ObservableProperty]` Generates an observable property from a private field. ```csharp using CommunityToolkit.Mvvm.ComponentModel; public partial class SampleViewModel : ObservableObject { [ObservableProperty] private string? name; } ``` Generated (simplified): ```csharp public string? Name { get => name; set { if (!EqualityComparer.Default.Equals(name, value)) { string? oldValue = name; OnNameChanging(value); OnNameChanging(oldValue, value); OnPropertyChanging(); name = value; OnNameChanged(value); OnNameChanged(oldValue, value); OnPropertyChanged(); } } } partial void OnNameChanging(string? value); partial void OnNameChanging(string? oldValue, string? newValue); partial void OnNameChanged(string? value); partial void OnNameChanged(string? oldValue, string? newValue); ``` ### Naming - Field `name` → property `Name` - Field `_name` → property `Name` - Field `m_name` → property `Name` - Field `Name` (PascalCase) → **error** (collides with generated property) ### Hooks Implement any subset of the partial methods. Unimplemented hooks are elided by the compiler — zero runtime cost. ```csharp [ObservableProperty] private ChildViewModel? selectedItem; partial void OnSelectedItemChanging(ChildViewModel? oldValue, ChildViewModel? newValue) { if (oldValue is not null) oldValue.IsSelected = false; if (newValue is not null) newValue.IsSelected = true; } ``` The hook methods are `partial` with no body declaration — you cannot add an explicit accessibility (no `public`/`private`). --- ## `[NotifyPropertyChangedFor(nameof(Other))]` Raises `PropertyChanged` for additional properties when this field changes. Stack multiple attributes for multiple targets. ```csharp [ObservableProperty] [NotifyPropertyChangedFor(nameof(FullName))] [NotifyPropertyChangedFor(nameof(Initials))] private string? firstName; ``` Use it for derived/computed properties: ```csharp public string FullName => $"{FirstName} {LastName}"; public string Initials => $"{FirstName?[0]}{LastName?[0]}"; ``` --- ## `[NotifyCanExecuteChangedFor(nameof(MyCommand))]` Calls `MyCommand.NotifyCanExecuteChanged()` when this field changes. The target must be an `IRelayCommand` (or `IAsyncRelayCommand`) property. ```csharp [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(SaveCommand))] [NotifyCanExecuteChangedFor(nameof(SubmitCommand))] private string? name; [RelayCommand(CanExecute = nameof(CanSave))] private Task SaveAsync() => repo.SaveAsync(Name!); private bool CanSave() => !string.IsNullOrWhiteSpace(Name); ``` > **`MVVMTK0016`** is raised if the target is not an accessible > `IRelayCommand` property in the same type. --- ## `[NotifyDataErrorInfo]` Only valid in types that inherit from `ObservableValidator`. Adds a `ValidateProperty(value)` call inside the generated setter, so DataAnnotation validators run on every assignment. ```csharp using System.ComponentModel.DataAnnotations; public partial class RegistrationViewModel : ObservableValidator { [ObservableProperty] [NotifyDataErrorInfo] [Required, MinLength(2), MaxLength(100)] private string? name; [ObservableProperty] [NotifyDataErrorInfo] [Required, EmailAddress] private string? email; } ``` Only attributes that derive from `ValidationAttribute` are forwarded to the generated property. Other attributes are ignored unless you use `[property: ]` (see below). --- ## `[NotifyPropertyChangedRecipients]` Only valid in types that inherit from `ObservableRecipient`. Adds a `Broadcast(oldValue, newValue)` call after a successful set, sending a `PropertyChangedMessage` to all recipients of the active `IMessenger`. ```csharp public partial class SelectionViewModel : ObservableRecipient { [ObservableProperty] [NotifyPropertyChangedRecipients] private Item? selectedItem; } ``` Subscribers can listen with: ```csharp WeakReferenceMessenger.Default.Register>( this, static (r, m) => { if (m.PropertyName == nameof(SelectionViewModel.SelectedItem)) r.Handle(m.NewValue); }); ``` --- ## `[RelayCommand]` Generates a lazy `RelayCommand` / `AsyncRelayCommand` from an instance method. Exposes it via the `IRelayCommand` / `IAsyncRelayCommand` interface. ```csharp [RelayCommand] private void Refresh() => Items.Reset(); ``` ```csharp private RelayCommand? refreshCommand; public IRelayCommand RefreshCommand => refreshCommand ??= new RelayCommand(Refresh); ``` ### Naming - `Refresh` → `RefreshCommand` - `OnRefresh` → `RefreshCommand` (leading `On` stripped) - `LoadAsync` → `LoadCommand` (trailing `Async` stripped) - `OnLoadAsync` → `LoadCommand` (both stripped) ### Sync with parameter ```csharp [RelayCommand] private void GreetUser(User user) => Console.WriteLine($"Hello {user.Name}"); ``` Generates `IRelayCommand GreetUserCommand` (a typed command). ### Async without cancellation ```csharp [RelayCommand] private async Task GreetUserAsync() { var user = await users.GetCurrentAsync(); Console.WriteLine($"Hello {user.Name}"); } ``` Generates `IAsyncRelayCommand GreetUserCommand` backed by `AsyncRelayCommand`. ### Async with cancellation ```csharp [RelayCommand] private async Task GreetUserAsync(CancellationToken token) { try { var user = await users.GetCurrentAsync(token); Console.WriteLine($"Hello {user.Name}"); } catch (OperationCanceledException) { /* expected */ } } ``` The toolkit propagates a `CancellationToken` to the wrapped method. Calling `GreetUserCommand.Cancel()` signals it. ### `IncludeCancelCommand = true` Generates a paired `XxxCancelCommand` whose `CanExecute` is wired to the underlying async command's `IsRunning` state — bind it to a Cancel button: ```csharp [RelayCommand(IncludeCancelCommand = true)] private async Task DownloadAsync(CancellationToken token) { /* ... */ } ``` ```xml