wpf-402989-mvvm-framework-viewmodels-compile-time-generated-viewmodels.md
The DevExpress MVVM Framework includes a source generator that produces boilerplate code for your View Models at compile time. You first need to define a stub class that implements View Model logic. Use specially designed attributes to indicate which members require extended boilerplate code. For example, you can mark fields that require standard property getter and setter implementations. Our MVVM Framework then analyzes and extends your code to generate the final View Model class. Consider the following example.
View Example: View Models Generated at Compile Time
Base View Model
Create a partial class. Add attributes to the class and its fields/methods:
using DevExpress.Mvvm.CodeGenerators;
[GenerateViewModel]
partial class ViewModel {
[GenerateProperty]
string username;
[GenerateProperty]
string status;
[GenerateCommand]
void Login() => Status = "User: " + Username;
bool CanLogin() => !string.IsNullOrEmpty(Username);
}
Generated View Model
The generator inspects the base View Model and produces a partial class that complements your implementation with the following boilerplate code:
You can view and debug the generated View Model:
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
username = value;
RaisePropertyChanged(UsernameChangedEventArgs);
}
}
public string? Status {
get => status;
set {
if(EqualityComparer<string?>.Default.Equals(status, value)) return;
status = value;
RaisePropertyChanged(StatusChangedEventArgs);
}
}
DelegateCommand? loginCommand;
public DelegateCommand LoginCommand {
get => loginCommand ??= new DelegateCommand(Login, CanLogin, true);
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
static PropertyChangedEventArgs StatusChangedEventArgs = new PropertyChangedEventArgs(nameof(Status));
}
Your project should meet the following requirements:
Otherwise, use Runtime-generated POCO View Models instead.
Note
C# 9 is officially supported in .NET 5 and newer. You may encounter issues when you use earlier versions of .NET and .NET Framework.
Prepare your project as outlined below to enable support for View Models generated at compile time:
We recommend that you configure the packages.config file to install NuGet Packages. If you use PackageReference, follow the steps below to include the code generator in a .NET Framework project:
Open the project file.
Remove XML code used to add the package:
Download the DevExpress.Mvvm.CodeGenerators.XX.Y.Z.dll file from GitHub Releases to the preferred folder.
Specify the path to the analyzer:
Save changes and reload the project.
You can access generated code only from Visual Studio. Use the Peek Definition command (F12) or search the generated file under Dependencies in Solution Explorer.
Declare a namespace as follows to access attributes:
using DevExpress.Mvvm.CodeGenerators;
GenerateViewModel
Applies to a class. Indicates that the source generator should process this class and produce View Model boilerplate code.
| Property | Type | Description |
|---|---|---|
| ImplementINotifyPropertyChanging | bool | Implements INotifyPropertyChanging. |
| ImplementIDataErrorInfo | bool | Implements IDataErrorInfo that allows you to validate data. |
| ImplementISupportServices | bool | Implements ISupportServices that allows you to include the Services mechanism to your View Model. |
| ISupportParentViewModel | bool | Implements ISupportParentViewModel that allows you to establish a parent-child relationship between View Models. |
GenerateProperty
Applies to a field. The source generator produces boilerplate code for the property getter and setter based on the field declaration.
| Property | Type | Description |
|---|---|---|
| IsVirtual | bool | Assigns a virtual modifier to the property. |
| OnChangedMethod | string? | Specifies the name of the method invoked after the property value is changed. If the property is not specified, the method’s name should follow the On[PropertyName]Changed pattern. |
| OnChangingMethod | string? | Specifies the name of the method invoked when the property value is changing. If the property is not specified, the method’s name should follow the On[PropertyName]Changing pattern. |
| SetterAccessModifier | AccessModifier | Specifies an access modifier for a set accessor. The default value is the same as a property’s modifier. Available values: Public, Private, Protected, Internal, ProtectedInternal. |
GenerateCommand
Applies to a method. The source generator produces boilerplate code for a Command based on this method.
| Property | Type | Description |
|---|---|---|
| AllowMultipleExecution | bool | Specifies the allowMultipleExecution parameter in the AsyncCommand constructor. The default value is false. |
| UseCommandManager | bool | Specifies the useCommandManager parameter in the Command constructor. The default value is true. |
| CanExecuteMethod | string? | Specifies a custom CanExecute method name. If the property is not specified, the method’s name should follow the Can[ActionName] pattern. |
| Name | string? | Specifies a custom Command name. The default value is [ActionName]Command. |
All View Models generated at compile time implement the INotifyPropertyChanged interface:
[GenerateViewModel]
public partial class ViewModel {
[GenerateProperty]
string username;
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
username = value;
RaisePropertyChanged(UsernameChangedEventArgs);
}
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
}
If you implement an interface in a base View Model class, add the interface’s members to this class.
In the code sample below, the base View Model class implements INotifyPropertyChanged. The generator analyzes the implementation and searches the PropertyChanged event to raise it from the generated View Model. If the base View Model class does not include the PropertyChanged event, the generated class is empty.
[GenerateViewModel]
public partial class ViewModel : INotifyPropertyChanged {
[GenerateProperty]
string username;
public event PropertyChangedEventHandler PropertyChanged;
}
partial class ViewModel {
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
public string? UserName {
get => userName;
set {
if(EqualityComparer<string?>.Default.Equals(userName, value)) return;
userName = value;
RaisePropertyChanged(UserNameChangedEventArgs);
}
}
}
If you want to implement INotifyPropertyChanging, IDataErrorInfo, or ISupportServices, use GenerateViewModel attribute properties.
Set the ImplementINotifyPropertyChanging property to true :
[GenerateViewModel(ImplementINotifyPropertyChanging = true)]
public partial class ViewModel {
[GenerateProperty]
string username;
}
partial class ViewModel : INotifyPropertyChanged, INotifyPropertyChanging {
public event PropertyChangedEventHandler? PropertyChanged;
public event PropertyChangingEventHandler? PropertyChanging;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
protected void RaisePropertyChanging(PropertyChangingEventArgs e) => PropertyChanging?.Invoke(this, e);
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
RaisePropertyChanging(UsernameChangingEventArgs);
username = value;
RaisePropertyChanged(UsernameChangedEventArgs);
}
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
static PropertyChangingEventArgs UsernameChangingEventArgs = new PropertyChangingEventArgs(nameof(Username));
}
Set the ImplementIDataErrorInfo property to true :
[GenerateViewModel(ImplementIDataErrorInfo = true)]
public partial class ViewModel {
}
Generated View Models implement the IDataErrorInfo interface as follows:
partial class ViewModel : INotifyPropertyChanged, IDataErrorInfo {
public event PropertyChangedEventHandler? PropertyChanged;
string IDataErrorInfo.Error { get => string.Empty; }
string IDataErrorInfo.this[string columnName] { get => IDataErrorInfoHelper.GetErrorText(this, columnName); }
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
}
The IDataErrorInfoHelper class allows you to get an error based on specified DataAnnotation attributes.
Set the ImplementISupportServices property to true :
[GenerateViewModel(ImplementISupportServices = true)]
public partial class ServicesViewModel {
IMessageBoxService MessageBoxService => ServiceContainer.GetService<IMessageBoxService>(ServiceSearchMode.PreferParents);
[GenerateCommand]
void ShowMessage() => MessageBoxService.ShowMessage("Message");
}
partial class ServicesViewModel : INotifyPropertyChanged, ISupportServices {
public event PropertyChangedEventHandler? PropertyChanged;
IServiceContainer? serviceContainer;
protected IServiceContainer ServiceContainer { get => serviceContainer ??= new ServiceContainer(this); }
IServiceContainer ISupportServices.ServiceContainer { get => ServiceContainer; }
protected T? GetService<T>() where T : class => ServiceContainer.GetService<T>();
protected T GetRequiredService<T>() where T : class => ServiceContainer.GetRequiredService<T>();
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
DelegateCommand? showMessageCommand;
public DelegateCommand ShowMessageCommand => showMessageCommand ??= new DelegateCommand(ShowMessage, null, true);
}
Refer to the Services in Custom ViewModels topic for more information on how to use the Service mechanism in a View Model.
Your View Model can inherit from another class. In this case, implementations declared in a parent class might affect a generated View Model.
If the parent class implements INotifyPropertyChanged or INotifyPropertyChanging , the child class cannot raise neither the PropertyChanged nor PropertyChanging events. Ensure that the parent class includes the RaisePropertyChanged or RaisePropertyChanging methods that allow the child class to raise these events:
public class DataObjectBase : INotifyPropertyChanged, INotifyPropertyChanging {
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangingEventHandler PropertyChanging;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
// or protected void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected void RaisePropertyChanging(string propertyName) => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
// or protected void RaisePropertyChanging(PropertyChangingEventArgs e) => PropertyChanging?.Invoke(this, e);
}
[GenerateViewModel]
partial class ViewModel : DataObjectBase {
[GenerateProperty]
string username;
}
partial class ViewModel {
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
RaisePropertyChanging(nameof(Username));
username = value;
RaisePropertyChanged(UsernameChangedEventArgs);
}
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
}
Set the ImplementISupportParentViewModel property to true to establish a parent-child relationship between View Models. The ISupportParentViewModel interface allows you to access Services registered in the main View Model from the child View Models. You can override the OnParentViewModelChanged event in a base View Model. If a base View Model class is inherited from another class, you can override the OnParentViewModelChanged event in the ancestor.
Refer to the following topic for more information on the parent-child relationship: ViewModel relationships (ISupportParentViewModel).
To generate a property in your View Model, create a field in a base View Model and add the GenerateProperty attribute to the field. The field name should not start with a capital letter:
[GenerateViewModel]
public partial class ViewModel {
[GenerateProperty]
string username;
}
As a result, the generated View Model includes a property, a RaisePropertyChanged method, and PropertyChangedEventArgs for the generated property:
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
username = value;
RaisePropertyChanged(UsernameChangedEventArgs);
}
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
}
If your field includes additional attributes, the generated property copies them:
[GenerateViewModel]
partial class ViewModel {
[GenerateProperty]
[StringLength(100, MinimumLength = 5)]
string username;
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
[System.ComponentModel.DataAnnotations.StringLengthAttribute(100, MinimumLength = 5)]
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
username = value;
RaisePropertyChanged(UsernameChangedEventArgs);
}
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
}
You can define methods invoked when properties are changed. These methods should meet the following requirements:
Their names follow the On[PropertyName]Changed and On[PropertyName]Changing pattern.
They return void.
They have no parameters or a parameter of the same type as the property.
[GenerateViewModel(ImplementINotifyPropertyChanging = true)]
partial class ViewModel {
[GenerateProperty]
string username;
// void OnUsernameChanged() { }
void OnUsernameChanged(string oldUsername) {
//...
}
// void OnUsernameChanging() { }
void OnUsernameChanging(string newUsername) {
//...
}
}
partial class ViewModel : INotifyPropertyChanged, INotifyPropertyChanging {
public event PropertyChangedEventHandler? PropertyChanged;
public event PropertyChangingEventHandler? PropertyChanging;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
protected void RaisePropertyChanging(PropertyChangingEventArgs e) => PropertyChanging?.Invoke(this, e);
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
RaisePropertyChanging(UsernameChangingEventArgs);
OnUsernameChanging(value);
var oldValue = username;
username = value;
RaisePropertyChanged(UsernameChangedEventArgs);
OnUsernameChanged(oldValue);
}
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
static PropertyChangingEventArgs UsernameChangingEventArgs = new PropertyChangingEventArgs(nameof(Username));
}
If you want to specify a custom name, specify the OnChangedMethod or OnChangingMethod attribute property as follows:
[GenerateViewModel]
public partial class ViewModel {
[GenerateProperty(OnChangedMethod = nameof(CustomName))]
string username;
void CustomName() { }
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
public string? Username {
get => username;
set {
if(EqualityComparer<string?>.Default.Equals(username, value)) return;
username = value;
RaisePropertyChanged(UserNameChangedEventArgs);
CustomName();
}
}
static PropertyChangedEventArgs UsernameChangedEventArgs = new PropertyChangedEventArgs(nameof(Username));
}
To generate a Command in your View Model, create a method in a base View Model and add the GenerateCommand attribute. The method should return void if you create DelegateCommands.
[GenerateViewModel]
public partial class ViewModel {
[GenerateCommand]
void Save() {
//...
}
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
DelegateCommand? saveCommand;
public DelegateCommand SaveCommand {
get => saveCommand ??= new DelegateCommand(Save, null, true);
}
}
Create a CanExecute method if necessary. This method name should complete the Can[ActionName] pattern and have the same parameters as the Action method. If you require a custom name, specify the CanExecuteMethod attribute property.
If the source generator cannot find a method that matches the description, it passes null as the Command constructor’s canExecuteMethod parameter.
[GenerateViewModel]
public partial class ViewModel {
[GenerateCommand]
void Login(string parameter) {
//...
}
bool CanLogin(string parameter) {
//...
}
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
DelegateCommand? loginCommand;
public DelegateCommand LoginCommand {
get => loginCommand ??= new DelegateCommand(Login, CanLogin, true);
}
}
Use attribute properties to specify Command constructor parameters. The code sample below specifies a custom name for the Command and sets the UseCommandManager parameter to false. If you disable UseCommandManager , add the RaiseCanExecuteChanged method to the base View Model as follows:
[GenerateViewModel]
public partial class ViewModel {
[GenerateCommand(UseCommandManager = false, Name = "CustomName")]
void Show() {
//...
}
bool CanShow() {
//...
}
public void UpdateShowCommand() => CustomName.RaiseCanExecuteChanged();
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
DelegateCommand? customName;
public DelegateCommand CustomName {
get => customName ??= new DelegateCommand(Show, CanShow, false);
}
}
Declare a method that returns a Task to create an Asynchronous Command. Apply the GenerateCommand attribute.
[GenerateViewModel]
partial class ViewModel {
[GenerateCommand]
async Task CalculateAsync() {
//...
}
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
AsyncCommand? calculateAsyncCommand;
public AsyncCommand CalculateAsyncCommand {
get => calculateAsyncCommand ??= new AsyncCommand(CalculateAsync, null, false, true);
}
}
Use attribute properties to specify Command constructor parameters. The code sample below sets the AllowMultipleExecution parameter to true.
[GenerateViewModel]
partial class ViewModel {
[GenerateCommand (AllowMultipleExecution = true)]
async Task CalculateAsync() {
//...
}
}
partial class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
AsyncCommand? calculateAsyncCommand;
public AsyncCommand CalculateAsyncCommand {
get => calculateAsyncCommand ??= new AsyncCommand(CalculateAsync, null, true, true);
}
}
You can add XML comments to a field and method. The generated property or command copies these comments. The following members are not supported:
If you want to add comments to these members, declare them in a base View Model.
You can apply additional attributes to fields and methods. Generated properties and commands copy them. If you want to apply an attribute to a command, do not restrict attribute usage. Alternatively, you can specify your attribute as follows:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public sealed class AttributeClassName : Attribute {
// ...
}
You can also inherit your attribute class from another class. The child class copies attribute usage from the parent class if AttributeUsageAttribute.Inherited is set to true.
The View Model code generator supports third-party libraries. To use a third-party library, apply attributes defined in the corresponding namespace to a base View Model class. You can refer only to one library within your class.
Install the Prism.Wpf NuGet package to use the Prism Library.
Declare a namespace as follows to access attributes:
using DevExpress.Mvvm.CodeGenerators.Prism;
GenerateViewModel
Applies to a class. Indicates that the source generator should process this class and produce View Model boilerplate code.
| Property | Type | Description |
|---|---|---|
| ImplementINotifyPropertyChanging | bool | Implements INotifyPropertyChanging. |
| ImplementIActiveAware | bool | Implements IActiveAware that notifies you when the View becomes active or inactive. |
GenerateProperty
Applies to a field. The source generator produces boilerplate code for the property getter and setter based on the field declaration.
| Property | Type | Description |
|---|---|---|
| IsVirtual | bool | Assigns a virtual modifier to the property. |
| OnChangedMethod | string? | Specifies the name of the method invoked after the property value is changed. If the property is not specified, the method’s name should follow the On[PropertyName]Changed pattern. |
| OnChangingMethod | string? | Specifies the name of the method invoked when the property value is changing. If the property is not specified, the method’s name should follow the On[PropertyName]Changing pattern. |
| SetterAccessModifier | AccessModifier | Specifies an access modifier for a set accessor. The default value is the same as a property’s modifier. Available values: Public, Private, Protected, Internal, ProtectedInternal. |
GenerateCommand
Applies to a method. The source generator produces boilerplate code for a Command based on this method.
| Property | Type | Description |
|---|---|---|
| ObservesCanExecuteProperty | string? | Specifies the ObservesCanExecute method for the supplied property. This method listens to property changes and uses the property as the CanExecute delegate. |
| ObservesProperties | string[]? | Specifies the ObservesProperty methods for all supplied properties. Each method calls the CanExecute method when the corresponding property value changes. |
| CanExecuteMethod | string? | Specifies a custom CanExecute method name. If the property is not specified, the method’s name should follow the Can[ActionName] pattern. |
| Name | string? | Specifies a custom Command name. The default value is [ActionName]Command. |
Declare a method that returns a Task to create an asynchronous command. Apply the GenerateCommand attribute.
[GenerateViewModel]
partial class ViewModel {
[GenerateCommand]
public Task WithNoArg() => Task.CompletedTask;
[GenerateCommand]
public Task WithArg(int? arg) => Task.CompletedTask;
}
partial class ViewModel : INotifyPropertyChanged {
DelegateCommand? withNoArgCommand;
public DelegateCommand WithNoArgCommand => withNoArgCommand ??= new DelegateCommand(async () => await WithNoArg());
DelegateCommand<int?>? withArgCommand;
public DelegateCommand<int?> WithArgCommand => withArgCommand ??= new DelegateCommand<int?>(async (arg) => await WithArg(arg));
}
You can define the method that is invoked when the IsActive property is changed. This method should meet the following requirements:
The generator analyzes the implementation and searches OnIsActiveChanged() to raise it from the generated View Model:
[GenerateViewModel(ImplementIActiveAware = true)]
partial class ViewModel {
// ...
void OnIsActiveChanged() {
// ...
}
}
partial class ViewModel : INotifyPropertyChanged, IActiveAware {
// ...
bool isActive;
public bool IsActive {
get => isActive;
set {
isActive = value;
OnIsActiveChanged();
IsActiveChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler? IsActiveChanged;
// ...
}
Tip
If you work with the MVVM Toolkit (the official replacement for the MVVM Light Toolkit), you can use its built-in source generators that were shipped in v8.0.0: MVVM Toolkit source generators.
Install the MvvmLight NuGet package to use the MVVM Light Toolkit.
Declare a namespace as follows to access attributes:
using DevExpress.Mvvm.CodeGenerators.MvvmLight;
GenerateViewModel
Applies to a class. Indicates that the source generator should process this class and produce View Model boilerplate code.
| Property | Type | Description |
|---|---|---|
| ImplementINotifyPropertyChanging | bool | Implements INotifyPropertyChanging. |
| ImplementICleanup | bool | Implements the ICleanup interface that allows you to clean your View Model (for example, flush its state to persistent storage, close the stream). |
GenerateProperty
Applies to a field. The source generator produces boilerplate code for the property getter and setter based on the field declaration.
| Property | Type | Description |
|---|---|---|
| IsVirtual | bool | Assigns a virtual modifier to the property. |
| OnChangedMethod | string? | Specifies the name of the method invoked after the property value is changed. If the property is not specified, the method’s name should follow the On[PropertyName]Changed pattern. |
| OnChangingMethod | string? | Specifies the name of the method invoked when the property value is changing. If the property is not specified, the method’s name should follow the On[PropertyName]Changing pattern. |
| SetterAccessModifier | AccessModifier | Specifies an access modifier for a set accessor. The default value is the same as a property’s modifier. Available values: Public, Private, Protected, Internal, ProtectedInternal. |
GenerateCommand
Applies to a method. The source generator produces boilerplate code for a Command based on this method.
| Property | Type | Description |
|---|---|---|
| CanExecuteMethod | string? | Specifies a custom CanExecute method name. If the property is not specified, the method’s name should follow the Can[ActionName] pattern. |
| Name | string? | Specifies a custom Command name. The default value is [ActionName]Command. |
Declare a method that returns a Task to create an asynchronous command. Apply the GenerateCommand attribute.
[GenerateViewModel]
partial class ViewModel {
[GenerateCommand]
public Task WithNoArg() => Task.CompletedTask;
[GenerateCommand]
public Task WithArg(int arg) => Task.CompletedTask;
}
partial class ViewModel : INotifyPropertyChanged {
RelayCommand? withNoArgCommand;
public RelayCommand WithNoArgCommand => withNoArgCommand ??= new RelayCommand(async () => await WithNoArg(), null);
RelayCommand<int>? withArgCommand;
public RelayCommand<int> WithArgCommand => withArgCommand ??= new RelayCommand<int>(async (arg) => await WithArg(arg), null);
}
You can define the method that is invoked when the View Model is cleaned up. This method should meet the following requirements:
The generator analyzes the implementation and searches OnCleanup() to raise it from the generated View Model:
[GenerateViewModel(ImplementICleanup = true)]
partial class ViewModel {
// ...
void OnCleanup() {
// ...
}
}
partial class ViewModel : INotifyPropertyChanged, ICleanup {
// ...
public virtual void Cleanup() {
MessengerInstance.Unregister(this);
OnCleanup();
}
// ...
}