View Models Generated at Compile Time
- 18 minutes to read
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.
- 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:
- Properties
- Property change notifications
- Command declarations
- INotifyPropertyChanged, INotifyPropertyChanging, IDataErrorInfo, ISupportServices implementation
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)); }
Prerequisites
Your project should meet the following requirements:
- C# 9+ (VB is not supported)
- .NET Framework v4.6.1+ or .NET Core v3.0+ (.NET 5 and later is recommended)
- Visual Studio v16.9.0+
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
Prepare your project as outlined below to enable support for View Models generated at compile time:
- Add a reference to DevExpress.Mvvm.v21.1+ or install the DevExpress.Mvvm NuGet package.
- Install the DevExpress.Mvvm.CodeGenerators NuGet package in your project.
Set the language version to 9 in the .csproject file:
<PropertyGroup> <LangVersion>9</LangVersion> </PropertyGroup>
For .NET Core projects, set the IncludePackageReferencesDuringMarkupCompilation property to true:
<PropertyGroup> <IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation> </PropertyGroup>
NuGet Package Installation Notes
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:
<PackageReference Include="DevExpress.Mvvm.CodeGenerators"> <Version>XX.Y.Z</Version> </PackageReference>
Download the DevExpress.Mvvm.CodeGenerators.XX.Y.Z.dll file from GitHub Releases to the preferred folder.
Specify the path to the analyzer:
<ItemGroup> <Analyzer Include="[PATH_TO_YOUR_FOLDER]\DevExpress.Mvvm.CodeGenerators.XX.Y.Z.dll" /> </ItemGroup>
Save changes and reload the project.
Review Generated View Model Class
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.
Apply Attributes to the Base View Model Class
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.
Implement Interfaces
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.
Implement INotifyPropertyChanging
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));
}
Implement IDataErrorInfo
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.
Implement ISupportServices
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.
Interface Implementation Notes for Inherited View Model Classes
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));
}
View Model Parent-Child Relationships
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).
Declare Fields to Generate Properties
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));
}
Property Change Notifications
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));
}
Turn Methods into Commands
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);
}
}
Asynchronous Commands
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);
}
}
Add XML Comments
You can add XML comments to a field and method. The generated property or command copies these comments. The following members are not supported:
- PropertyChanged events
- PropertyChanging events
- RaisePropertyChanged methods
- RaisePropertyChanging methods
- ParentViewModels
- ServiceContainers
If you want to add comments to these members, declare them in a base View Model.
Add Additional Attributes
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.
Third-party Library Support
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.
Prism Library
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.
Asynchronous Commands
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));
}
IActiveAware Implementation Notes
You can define the method that is invoked when the IsActive property is changed. This method should meet the following requirements:
- The method has the OnIsActiveChanged name.
- It returns void.
- It has no parameters.
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;
// ...
}
MVVM Light Toolkit
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.
Asynchronous Commands
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);
}
ICleanup Implementation Notes
You can define the method that is invoked when the View Model is cleaned up. This method should meet the following requirements:
- The method has the OnCleanup name.
- It returns void.
- It has no parameters.
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();
}
// ...
}