MvvmGen.PureCodeGeneration
1.3.0
dotnet add package MvvmGen.PureCodeGeneration --version 1.3.0
NuGet\Install-Package MvvmGen.PureCodeGeneration -Version 1.3.0
<PackageReference Include="MvvmGen.PureCodeGeneration" Version="1.3.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add MvvmGen.PureCodeGeneration --version 1.3.0
#r "nuget: MvvmGen.PureCodeGeneration, 1.3.0"
// Install MvvmGen.PureCodeGeneration as a Cake Addin #addin nuget:?package=MvvmGen.PureCodeGeneration&version=1.3.0 // Install MvvmGen.PureCodeGeneration as a Cake Tool #tool nuget:?package=MvvmGen.PureCodeGeneration&version=1.3.0
⚡ MvvmGen - a Lightweight and Modern MVVM library
MvvmGen is a lightweight and modern library that helps you to apply the popular Model-View-ViewModel-pattern (MVVM) in your XAML applications. MvvmGen works for your apps that you build with WPF, WinUI, Xamarin.Forms, .NET MAUI, Uno Platform, AvaloniaUI, or any other .NET stack.
MvvmGen was created from ground up with a focus on C# Source Generators. With MvvmGen, a lot of your ViewModel code gets generated behind the scenes while you're typing code in your code editor. This makes MvvmGen the most productive MVVM library.
What's in the Package?
MvvmGen consists of three parts:
- Framework classes needed to apply the MVVM pattern
- A
ViewModelBase
class that implementsINotifyPropertyChanged
- A
DelegateCommand
class that implementsICommand
- An
EventAggregator
class for ViewModel communication
- A
- Attributes to decorate your ViewModels
- The
[ViewModel]
attribute is the most popular attribute, as it marks a class as a ViewModel. With this attribute set on a class, the source generator knows there's something to generate - There are several other attributes, like
[Property]
,[Command]
, or[Inject]
, that tell the source generator what it should generate
- The
- A modern C# source generator
- This is your best friend who generates the ViewModel boilerplate for you behind the scenes
Getting Started
To get started with MvvmGen, install either the NuGet package MvvmGen or MvvmGen.PureCodeGeneration.
dotnet add package MvvmGen
dotnet add package MvvmGen.PureCodeGeneration
From a usage perspective, both packages work exactly the same. The difference is in the code generation.
- MvvmGen package - ViewModels are generated, but attributes and framework classes come from a referenced MvvmGen.dll that is part of the MvvmGen NuGet package.
- MvvmGen.PureCodeGeneration package - Not only ViewModels are generated, but also attributes and framework classes. This means that your compiled assembly does not have a dependency on an MvvmGen.dll, as all the MvvmGen-specific code is generated at compile time.
If you need help for a decision: The MvvmGen package is the more popular package. But as the API of both packages is exactly the same, you can swap one for the other at any time.
Creating a ViewModel
To create a ViewModel with MvvmGen, you create a partial class that you decorate with the ViewModel
attribute:
using MvvmGen;
namespace MyWpfApp.ViewModel
{
[ViewModel]
public partial class EmployeeViewModel { }
}
Because of the ViewModel
attribute, MvvmGen's source generator will generate another partial class behind the scenes like the one below:
using MvvmGen.Commands;
using MvvmGen.Events;
using MvvmGen.ViewModels;
namespace MyWpfApp.ViewModel
{
partial class EmployeeViewModel : global::MvvmGen.ViewModels.ViewModelBase
{
public EmployeeViewModel()
{
this.OnInitialize();
}
partial void OnInitialize();
}
}
Next, let's generate some properties.
Generate Properties
To generate properties, you decorate your private fields with MvvmGen's Property
attribute:
using MvvmGen;
namespace MyWpfApp.ViewModel
{
[ViewModel]
public partial class EmployeeViewModel
{
[Property] string _firstName;
[Property] string _lastName;
}
}
Below you can see the generated code. It contains the two properties FirstName
and LastName
.
In the setters of these properties the PropertyChanged
event is raised by calling
the OnPropertyChanged
method that is defined in the ViewModelBase
class.
This event notifies data bindings in the user interface about property changes.
using MvvmGen.Commands;
using MvvmGen.Events;
using MvvmGen.ViewModels;
namespace MyWpfApp.ViewModel
{
partial class EmployeeViewModel : global::MvvmGen.ViewModels.ViewModelBase
{
public EmployeeViewModel()
{
this.OnInitialize();
}
partial void OnInitialize();
public string FirstName
{
get => _firstName;
set
{
if (_firstName != value)
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
}
public string LastName
{
get => _lastName;
set
{
if (_lastName != value)
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
}
}
}
So, as you can see in the code snippet above, all the property boilerplate is generated for you. Now, let's also generate a command.
Generate Commands
To generate a command, you decorate a method with the Command
attribute like you see it
in the code snippet below. If you have some can-execute logic, you set the attribute's optional
CanExecuteMethod
property. In the code snippet below it's set to the CanSave
method.
On this CanSave
method there's a CommandInvalidate
attribute that ensures in this case
that the command's CanExecuteChanged
event is raised everytime the ViewModel's FirstName
property
was changed.
using MvvmGen;
namespace MyWpfApp.ViewModel
{
[ViewModel]
public partial class EmployeeViewModel
{
[Property] string _firstName;
[Property] string _lastName;
[Command(CanExecuteMethod = nameof(CanSave))]
private void Save() { }
[CommandInvalidate(nameof(FirstName))]
private bool CanSave()
{
return !string.IsNullOrEmpty(FirstName);
}
}
}
Below you see the generated code that contains now a SaveCommand
property that gets initialized
with an instance of MvvmGen's DelegateCommand
class. The DelegateCommand
instance points to the
methods Save
and CanSave
defined in your code. There's also an InvalidateCommands
method
that raises the command's CanExecuteChanged
event if the FirstName
property was changed.
using MvvmGen.Commands;
using MvvmGen.Events;
using MvvmGen.ViewModels;
namespace MyWpfApp.ViewModel
{
partial class EmployeeViewModel : global::MvvmGen.ViewModels.ViewModelBase
{
private IDelegateCommand? _saveCommand;
public EmployeeViewModel()
{
this.OnInitialize();
}
partial void OnInitialize();
public IDelegateCommand SaveCommand => _saveCommand ??= new DelegateCommand(_ => Save(), _ => CanSave());
public string FirstName { ... }
public string LastName { ... }
protected override void InvalidateCommands(string? propertyName)
{
base.InvalidateCommands(propertyName);
if (propertyName == "FirstName")
{
SaveCommand.RaiseCanExecuteChanged();
}
}
}
}
Next, let's also inject and use a service.
Inject Services
With MvvmGen's Inject
attribute you can inject one or more services into your ViewModel. In the code snippet below
an IEventAggregator
is injected. In the Save
method the injected service is used to publish an event.
using MvvmGen;
using MvvmGen.Events;
namespace MyWpfApp.ViewModel
{
public record EmployeeSavedEvent(string FirstName, string LastName);
[Inject(typeof(IEventAggregator))]
[ViewModel]
public partial class EmployeeViewModel
{
[Property] private string _firstName;
[Property] private string _lastName;
[Command(CanExecuteMethod = nameof(CanSave))]
private void Save()
{
EventAggregator.Publish(new EmployeeSavedEvent(FirstName, LastName));
}
[CommandInvalidate(nameof(FirstName))]
private bool CanSave()
{
return !string.IsNullOrEmpty(FirstName);
}
}
}
Below you can see the generated code. As you can see, there's a new constructor parameter
of type IEventAggregator
. The parameter is stored in an EventAggregator
property that
you can use in your code like demonstrated in the Save
method in the code snippet above.
using MvvmGen.Commands;
using MvvmGen.Events;
using MvvmGen.ViewModels;
namespace MyWpfApp.ViewModel
{
partial class EmployeeViewModel : global::MvvmGen.ViewModels.ViewModelBase
{
private IDelegateCommand? _saveCommand;
public EmployeeViewModel(MvvmGen.Events.IEventAggregator eventAggregator)
{
this.EventAggregator = eventAggregator;
this.OnInitialize();
}
...
protected MvvmGen.Events.IEventAggregator EventAggregator { get; private set; }
...
}
}
Learning More
Now you learned how to use some basic features of the MvvmGen library to build and generate ViewModels. If you want to learn more about MvvmGen:
- Take a look at the official docs, which are in the docs folder of the MvvmGen repository
- Browse the MvvmGen Samples repository. It contains for example the very popular EmployeeManager application built with WPF and WinUI that has a tabbed user interface.
Questions and Feedback
If you have any questions or feedback, you can always open an issue in the MvvmGen repository or you can also contact me, the author and maintainer of MvvmGen, via
Happy coding,
Thomas Claudius Huber (https://www.thomasclaudiushuber.com)
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version 1.3.0:
- New CommandType property on [ViewModel] attribute to specify a custom IDelegateCommand implementation.
- New ModelPropertyName property on [ViewModel] attribute to set the name of the property that contains the wrapped model specified via the ModelType property.
- New ModelPropertiesToIgnore property on [ViewModel] attribute to set a comma-separated list of model properties that should not be generated for the model specified via the ModelType property.
- Fix nullable warnings when generating command properties.
- Fix interface generation when ViewModel has generic methods.
- Fix constructor generation when base ViewModel has [Inject] attributes.
Version 1.2.1:
- Ensure that [ViewModelGenerateInterface] also generates command properties
Version 1.2.0:
- New [ViewModelGenerateInterface] attribute to generate an interface for a ViewModel. This supports more unit testing scenarios
- If [ViewModelGenerateInterface] is set, [ViewModelGenerateFactory] will return the interface type instead of the ViewModel type.
- New ReturnType property on [ViewModelGenerateFactory] attribute allows you to explicitly define a return type of the factory.
- Source generator implements now the newer IIncrementalGenerator interface
- The model used by the source generator implements Equals to support caching between generation steps. This makes the generator more performant, which is especially noticable in larger solutions.
- Bug fix for [ViewModelGenerateFactory] attribute. When injecting an IEventAggregator into the ViewModel, the factory now also has the correct constructor parameters.
- Minor code optimizations