Dotnet Communitytoolkit MVVM - Wei Zhi
Dotnet Communitytoolkit MVVM - Wei Zhi
Platform and Runtime Independent - .NET Standard 2.0, .NET Standard 2.1 and
.NET 6 🚀 (UI Framework Agnostic)
Simple to pick-up and use - No strict requirements on Application structure or
coding-paradigms (outside of 'MVVM'ness), i.e., flexible usage.
À la carte - Freedom to choose which components to use.
Reference Implementation - Lean and performant, providing implementations for
interfaces that are included in the Base Class Library, but lack concrete types to use
them directly.
The MVVM Toolkit is maintained and published by Microsoft, and part of the .NET
Foundation. It is also used by several first party applications that are built into Windows,
such as the Microsoft Store .
This package targets .NET Standard so it can be used on any app platform: UWP,
WinForms, WPF, Xamarin, Uno, and more; and on any runtime: .NET Native, .NET Core,
.NET Framework, or Mono. It runs on all of them. The API surface is identical in all cases,
making it perfect for building shared libraries.
Additionally, the MVVM Toolkit also has a .NET 6 target, which is used to enable more
internal optimizations when running on .NET 6. The public API surface is identical in
both cases, so NuGet will always resolve the best possible version of the package
without consumers having to worry about which APIs will be available on their platform.
Getting started
To install the package from within Visual Studio:
1. In Solution Explorer, right-click on the project and select Manage NuGet Packages.
Search for CommunityToolkit.Mvvm and install it.
2. Add a using or Imports directive to use the new APIs:
c#
using CommunityToolkit.Mvvm;
VB
Imports CommunityToolkit.Mvvm
3. Code samples are available in the other docs pages for the MVVM Toolkit, and in
the unit tests for the project.
CommunityToolkit.Mvvm.ComponentModel
ObservableObject
ObservableRecipient
ObservableValidator
CommunityToolkit.Mvvm.DependencyInjection
Ioc
CommunityToolkit.Mvvm.Input
RelayCommand
RelayCommand<T>
AsyncRelayCommand
AsyncRelayCommand<T>
IRelayCommand
IRelayCommand<T>
IAsyncRelayCommand
IAsyncRelayCommand<T>
CommunityToolkit.Mvvm.Messaging
IMessenger
WeakReferenceMessenger
StrongReferenceMessenger
IRecipient<TMessage>
MessageHandler<TRecipient, TMessage>
CommunityToolkit.Mvvm.Messaging.Messages
PropertyChangedMessage<T>
RequestMessage<T>
AsyncRequestMessage<T>
CollectionRequestMessage<T>
AsyncCollectionRequestMessage<T>
ValueChangedMessage<T>
This package aims to offer as much flexibility as possible, so developers are free to
choose which components to use. All types are loosely-coupled, so that it's only
necessary to include what you use. There is no requirement to go "all-in" with a specific
series of all-encompassing APIs, nor is there a set of mandatory patterns that need to be
followed when building apps using these helpers. Combine these building blocks in a
way that best fits your needs.
Additional resources
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
MVVM source generators
Article • 07/15/2022
Starting with version 8.0, the MVVM Toolkit includes brand new Roslyn source
generators that will help greatly reduce boilerplate when writing code using the MVVM
architecture. They can simplify scenarios where you need to setup observable properties,
commands and more. If you're not familiar with source generators, you can read more
about them here. This is a simplified view of how they work:
This means that as you're writing code, the MVVM Toolkit generator will now take care
of generating additional code for you behind the scenes, so you don't have to worry
about it. This code will then be compiled and included in your application, so the end
result is exactly the same as if you had written all that extra code manually, but without
having to do all of that extra work! 🎉
C#
You could just have a simple annotated field to express the same?
C#
[ObservableProperty]
private string? name;
C#
C#
[RelayCommand]
private void SayHello()
{
Console.WriteLine("Hello");
}
With the new MVVM source generators, all of this is possible, and much more! 🙌
7 Note
Source generators can be used independently from other existing features in the
MVVM Toolkit, and you're free to mix and match using source generators with
previous APIs as needed. That is, you're free to just gradually start using source
generators in new files and eventually migrating older files to use them to reduce
verbosity, but there is no obligation to always use either approach in a whole
project or application.
These docs will go over exactly what features are included with the MVVM generators
and how to use them:
CommunityToolkit.Mvvm.ComponentModel
ObservableProperty
INotifyPropertyChanged
CommunityToolkit.Mvvm.Input
RelayCommand
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
ObservableProperty attribute
Article • 05/02/2023
7 Note
In order to work, annotated fields need to be in a partial class with the necessary
INotifyPropertyChanged infrastructure. If the type is nested, all types in the
declaration syntax tree must also be annotated as partial. Not doing so will result in
a compile errors, as the generator will not be able to generate a different partial
declaration of that type with the requested observable property.
How it works
The ObservableProperty attribute can be used to annotate a field in a partial type, like
so:
C#
[ObservableProperty]
private string? name;
C#
It will also do so with an optimized implementation, so the end result will be even faster.
7 Note
The name of the generated property will be created based on the field name. The
generator assumes the field is named either lowerCamel , _lowerCamel or
m_lowerCamel , and it will transform that to be UpperCamel to follow proper .NET
naming conventions. The resulting property will always have public accessors, but
the field can be declared with any visibility ( private is recommended).
C#
This allows you to implement any of those methods to inject additional code. The first
two are useful whenever you want to run some logic that only needs to reference the
new value that the property has been set to. The other two are useful whenever you
have some more complex logic that also has to update some state on both the old and
new value being set.
For instance, here is an example of how the first two overloads can be used:
C#
[ObservableProperty]
private string? name;
And here is an example of how the other two overloads can be used:
C#
[ObservableProperty]
private ChildViewModel? selectedItem;
You're free to only implement any number of methods among the ones that are
available, or none of them. If they are not implemented (or if only one is), the entire
call(s) will just be removed by the compiler, so there will be no performance hit at all for
cases where this additional functionality is not required.
7 Note
The generated methods are partial methods with no implementation, meaning that
if you choose to implement them, you cannot specify an explicit accessibility for
them. That is, implementations of these methods should also be declared as just
partial methods, and they will always implicitly have private accessibility. Trying to
add an explicit accessibility (eg. adding public or private ) will result in an error, as
that is not allowed in C#.
C#
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FullName))]
private string? name;
C#
C#
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(MyCommand))]
private string? name;
C#
In order for this to work, the target command has to be some IRelayCommand property.
C#
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[MinLength(2)] // Any other validation attributes too...
private string? name;
C#
That generated ValidateProperty call will then validate the property and update the
state of the ObservableValidator object, so that UI components can react to it and
display any validation errors appropriately.
7 Note
C#
[ObservableProperty]
[NotifyPropertyChangedRecipients]
private string? name;
C#
That generated Broadcast call will then send a new PropertyChangedMessage<T> using
the IMessenger instance in use in the current viewmodel, to all registered subscribers.
C#
[ObservableProperty]
[property: JsonRequired]
[property: JsonPropertyName("name")]
private string? username;
This will generate a Username property, with those two [JsonRequired] and
[JsonPropertyName("name")] attributes over it. You can use as many attribute lists
targeting the property as you want, and all of them will be forwarded to the generated
properties.
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
RelayCommand attribute
Article • 05/02/2023
7 Note
How it works
The RelayCommand attribute can be used to annotate a method in a partial type, like so:
C#
[RelayCommand]
private void GreetUser()
{
Console.WriteLine("Hello!");
}
C#
7 Note
The name of the generated command will be created based on the method name.
The generator will use the method name and append "Command" at the end, and
it will strip the "On" prefix, if present. Additionally, for asynchronous methods, the
"Async" suffix is also stripped before "Command" is appeneded.
Command parameters
The [RelayCommand] attribute supports creating commands for methods with a
parameter. In that case, it will automatically change the generated command to be an
IRelayCommand<T> instead, accepting a parameter of the same type:
C#
[RelayCommand]
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
C#
The resulting command will automatically use the type of the argument as its type
argument.
Asynchronous commands
The [RelayCommand] command also supports wrapping asynchronous methods, via the
IAsyncRelayCommand and IAsyncRelayCommand<T> interfaces. This is handled
automatically whenever a method returns a Task type. For instance:
C#
[RelayCommand]
private async Task GreetUserAsync()
{
User user = await userService.GetCurrentUserAsync();
Console.WriteLine($"Hello {user.Name}!");
}
C#
If the method takes a parameter, the resulting command will also be generic.
There is a special case when the method has a CancellationToken, as that will be
propagated to the command to enable cancellation. That is, a method like this:
C#
[RelayCommand]
private async Task GreetUserAsync(CancellationToken token)
{
try
{
User user = await userService.GetCurrentUserAsync(token);
Console.WriteLine($"Hello {user.Name}!");
}
catch (OperationCanceledException)
{
}
}
Will result in the generated command passing a token to the wrapped method. This
allows consumers to just call IAsyncRelayCommand.Cancel to signal that token, and to
allow pending operations to be stopped correctly.
[RelayCommand(CanExecute = nameof(CanGreetUser))]
private void GreetUser(User? user)
{
Console.WriteLine($"Hello {user!.Name}!");
}
This way, CanGreetUser is invoked when the button is first bound to the UI (eg. to a
button), and then it is invoked again every time
IRelayCommand.NotifyCanExecuteChanged is invoked on the command.
For instance, this is how a command can be bound to a property to control its state:
C#
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(GreetUserCommand))]
private User? selectedUser;
XML
<!-- Note: this example uses traditional XAML binding syntax -->
<Button
Content="Greet user"
Command="{Binding GreetUserCommand}"
CommandParameter="{Binding SelectedUser}"/>
cause the wrapped CanGreetUser method to be evaluated, which will return the new
state for the button based on whether or not the input User instance (which in the UI is
bound to the SelectedUser property) is null or not. This means that whenever
SelectedUser is changed, GreetUserCommand will become enabled or not based on
whether that property has a value, which is the desired behavior in this scenario.
7 Note
The command will not automatically be aware of when the return value for the
CanExecute method or property has changed. It is up to the developer to call
IRelayCommand.NotifyCanExecuteChanged to invalidate the command and request
the linked CanExecute method to be evaluated again to then update the visual
state of the control bound to the command.
Note that if a command accepts a cancellation token, a token will also be canceled if a
concurrent execution is requested. The main difference is that if concurrent executions
are allowed, the command will remain enabled and it will start a new requested
execution without waiting for the previous one to actually complete.
Await and rethrow (default): when the command awaits the completion of an
invocation, any exceptions will naturally be thrown on the same synchronization
context. That usually means that exceptions being thrown would just crash the
app, which is a behavior consistent with that of synchronous commands (where
exceptions being thrown will also crash the app).
Flow exceptions to task scheduler: if a command is configured to flow exceptions
to the task scheduler, exceptions being thrown will not crash the app, but instead
they will both become available through the exposed
IAsyncRelayCommand.ExecutionTask as well as bubbling up to the
TaskScheduler.UnobservedTaskException. This enables more advanced scenarios
(such as having UI components bind to the task and display different results based
on the outcome of the operation), but it is more complex to use correctly.
The default behavior is having commands await and rethrow exceptions. This can be
configured via the FlowExceptionsToTaskScheduler property:
C#
[RelayCommand(FlowExceptionsToTaskScheduler = true)]
private async Task GreetUserAsync(CancellationToken token)
{
User user = await userService.GetCurrentUserAsync(token);
Console.WriteLine($"Hello {user.Name}!");
}
In this case, the try/catch is not needed, as exceptions will not crash the app anymore.
Note that this will also cause other unrelated exceptions to not be rethrown
automatically, so you should carefully decide how to approach each individual scenario
and configure the rest of the code appropriately.
C#
[RelayCommand(IncludeCancelCommand = true)]
private async Task DoWorkAsync(CancellationToken token)
{
// Do some long running work...
}
This will cause a DoWorkCancelCommand property to also be generated. This can then be
bound to some other UI component to easily let users cancel pending asynchronous
operations.
[RelayCommand]
[property: JsonIgnore]
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
This will generate a GreetUserCommand property, with the [JsonIgnore] attribute over it.
You can use as many attribute lists targeting the method as you want, and all of them
will be forwarded to the generated properties.
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
INotifyPropertyChanged attributes
Article • 08/10/2022
7 Note
In order to work, annotated types need to be in a partial class. If the type is nested,
all types in the declaration syntax tree must also be annotated as partial. Not doing
so will result in a compile errors, as the generator will not be able to generate a
different partial declaration of that type with the requested additional code.
7 Note
These attributes are only meant to be used in cases where the target types cannot
just inherit from the equivalent types (eg. from ObservableObject ). If that is
possible, inheriting is the recommended approach, as it will reduce the binary size
by avoiding creating duplicated code into the final assembly.
C#
[INotifyPropertyChanged]
public partial class MyViewModel : SomeOtherType
{
}
This will generate a complete INotifyPropertyChanged implementation into the
MyViewModel type, complete with additional helpers (such as SetProperty ) that can be
used to reduce verbosity. Here is a brief summary of the various attributes:
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
ObservableObject
Article • 04/07/2022
The ObservableObject is a base class for objects that are observable by implementing
the INotifyPropertyChanged and INotifyPropertyChanging interfaces. It can be used as a
starting point for all kinds of objects that need to support property change notifications.
How it works
ObservableObject has the following main features:
events.
It provides a series of SetProperty methods that can be used to easily set property
values from types inheriting from ObservableObject , and to automatically raise the
appropriate events.
It provides the SetPropertyAndNotifyOnCompletion method, which is analogous to
SetProperty but with the ability to set Task properties and raise the notification
events automatically when the assigned tasks are completed.
It exposes the OnPropertyChanged and OnPropertyChanging methods, which can be
overridden in derived types to customize how the notification events are raised.
Simple property
Here's an example of how to implement notification support to a custom property:
C#
C#
In this case we're using the SetProperty<TModel, T>(T, T, TModel, Action<TModel, T>,
string) overload. The signature is slightly more complex than the previous one - this is
necessary to let the code still be extremely efficient even if we don't have access to a
backing field like in the previous scenario. We can go through each part of this method
signature in detail to understand the role of the different components:
TModel is a type argument, indicating the type of the model we're wrapping. In this
case, it'll be our User class. Note that we don't need to specify this explicitly - the
C# compiler will infer this automatically by how we're invoking the SetProperty
method.
T is the type of the property we want to set. Similarly to TModel , this is inferred
automatically.
T oldValue is the first parameter, and in this case we're using user.Name to pass
7 Note
C#
public class MyModel : ObservableObject
{
private TaskNotifier<int>? requestTask;
7 Note
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
ObservableRecipient
Article • 04/07/2022
The ObservableRecipient type is a base class for observable objects that also acts as
recipients for messages. This class is an extension of ObservableObject which also
provides built-in support to use the IMessenger type.
How it works
The ObservableRecipient type is meant to be used as a base for viewmodels that also
use the IMessenger features, as it provides built-in support for it. In particular:
It has both a parameterless constructor and one that takes an IMessenger instance,
to be used with dependency injection. It also exposes a Messenger property that
can be used to send and receive messages in the viewmodel. If the parameterless
constructor is used, the WeakReferenceMessenger.Default instance will be assigned
to the Messenger property.
It exposes an IsActive property to activate/deactivate the viewmodel. In this
context, to "activate" means that a given viewmodel is marked as being in use,
such that eg. it will start listening for registered messages, perform other setup
operations, etc. There are two related methods, OnActivated and OnDeactivated ,
that are invoked when the property changes value. By default, OnDeactivated
automatically unregisters the current instance from all registered messages. For
best results and to avoid memory leaks, it's recommended to use OnActivated to
register to messages, and to use OnDeactivated to do cleanup operations. This
pattern allows a viewmodel to be enabled/disabled multiple times, while being
safe to collect without the risk of memory leaks every time it's deactivated. By
default, OnActivated will automatically register all the message handlers defined
through the IRecipient<TMessage> interface.
It exposes a Broadcast<T>(T, T, string) method which sends a
PropertyChangedMessage<T> message through the IMessenger instance available
from the Messenger property. This can be used to easily broadcast changes in the
properties of a viewmodel without having to manually retrieve a Messenger
instance to use. This method is used by the overload of the various SetProperty
methods, which have an additional bool broadcast property to indicate whether or
not to also send a message.
C#
In the example above, OnActivated automatically registers the instance as a recipient for
LoggedInUserRequestMessage messages, using that method as the action to invoke. Using
the IRecipient<TMessage> interface is not mandatory, and the registration can also be
done manually (even using just an inline lambda expression):
C#
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
ObservableValidator
Article • 02/01/2023
How it works
ObservableValidator has the following main features:
Simple property
Here's an example of how to implement a property that supports both change
notifications as well as validation:
C#
[Required]
[MinLength(2)]
[MaxLength(100)]
public string Name
{
get => name;
set => SetProperty(ref name, value, true);
}
}
modified.
C#
[Required]
[MinLength(2)]
[MaxLength(100)]
[CustomValidation(typeof(RegistrationForm), nameof(ValidateName))]
public string Name
{
get => this.name;
set => SetProperty(ref this.name, value, true);
}
if (isValid)
{
return ValidationResult.Success;
}
In this case we have a static ValidateName method that will perform validation on the
Name property through a service that is injected into our viewmodel. This method
receives the name property value and the ValidationContext instance in use, which
contains things such as the viewmodel instance, the name of the property being
validated, and optionally a service provider and some custom flags we can use or set. In
this case, we are retrieving the RegistrationForm instance from the validation context,
and from there we are using the injected service to validate the property. Note that this
validation will be executed next to the ones specified in the other attributes, so we are
free to combine custom validation methods and existing validation attributes however
we like.
above, as it makes it very easy to just reuse the same attribute in multiple places.
Suppose we wanted to validate a property based on its relative value with respect to
another property in the same viewmodel. The first step would be to define a custom
[GreaterThanAttribute] , like so:
C#
if (((IComparable)value).CompareTo(otherValue) > 0)
{
return ValidationResult.Success;
}
C#
[Range(10, 100)]
[GreaterThan(nameof(B))]
public int A
{
get => this.a;
set => SetProperty(ref this.a, value, true);
}
private int b;
[Range(20, 80)]
public int B
{
get => this.b;
set
{
SetProperty(ref this.b, value, true);
ValidateProperty(A, nameof(A));
}
}
}
In this case, we have two numerical properties that must be in a specific range and with
a specific relationship between each other ( A needs to be greater than B ). We have
added the new [GreaterThanAttribute] over the first property, and we also added a call
to ValidateProperty in the setter for B , so that A is validated again whenever B
changes (since its validation status depends on it). We just need these two lines of code
in our viewmodel to enable this custom validation, and we also get the benefit of having
a reusable custom validation attribute that could be useful in other viewmodels in our
application as well. This approach also helps with code modularization, as the validation
logic is now completely decoupled from the viewmodel definition itself.
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
RelayCommand and
RelayCommand<T>
Article • 04/07/2022
C#
XML
<Page
x:Class="MyApp.Views.MyPage"
xmlns:viewModels="using:MyApp.ViewModels">
<Page.DataContext>
<viewModels:MyViewModel x:Name="ViewModel"/>
</Page.DataContext>
<StackPanel Spacing="8">
<TextBlock Text="{x:Bind ViewModel.Counter, Mode=OneWay}"/>
<Button
Content="Click me!"
Command="{x:Bind ViewModel.IncrementCounterCommand}"/>
</StackPanel>
</Page>
The Button binds to the ICommand in the viewmodel, which wraps the private
IncrementCounter method. The TextBlock displays the value of the Counter property
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
AsyncRelayCommand and
AsyncRelayCommand<T>
Article • 07/15/2022
They expose an ExecutionTask property that can be used to monitor the progress
of a pending operation, and an IsRunning that can be used to check when an
operation completes. This is particularly useful to bind a command to UI elements
such as loading indicators.
They implement the IAsyncRelayCommand and IAsyncRelayCommand<T>
interfaces, which means that viewmodel can easily expose commands using these
to reduce the tight coupling between types. For instance, this makes it easier to
replace a command with a custom implementation exposing the same public API
surface, if needed.
C#
XML
<Page
x:Class="MyApp.Views.MyPage"
xmlns:viewModels="using:MyApp.ViewModels"
xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters">
<Page.DataContext>
<viewModels:MyViewModel x:Name="ViewModel"/>
</Page.DataContext>
<Page.Resources>
<converters:TaskResultConverter x:Key="TaskResultConverter"/>
</Page.Resources>
Upon clicking the Button , the command is invoked, and the ExecutionTask updated.
When the operation completes, the property raises a notification which is reflected in
the UI. In this case, both the task status and the current result of the task are displayed.
Note that to show the result of the task, it is necessary to use the
TaskExtensions.GetResultOrDefault method - this provides access to the result of a task
that has not yet completed without blocking the thread (and possibly causing a
deadlock).
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
Ioc (Inversion of control )
Article • 07/15/2022
The MVVM Toolkit doesn't provide built-in APIs to facilitate the usage of this pattern, as
there already exist dedicated libraries specifically for this such as the
Microsoft.Extensions.DependencyInjection package, which provides a fully featured and
powerful DI set of APIs, and acts as an easy to setup and use IServiceProvider . The
following guide will refer to this library and provide a series of examples of how to
integrate it into applications using the MVVM pattern.
C#
this.InitializeComponent();
}
/// <summary>
/// Gets the current <see cref="App"/> instance in use
/// </summary>
public new static App Current => (App)Application.Current;
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve
application services.
/// </summary>
public IServiceProvider Services { get; }
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IFilesService, FilesService>();
services.AddSingleton<ISettingsService, SettingsService>();
services.AddSingleton<IClipboardService, ClipboardService>();
services.AddSingleton<IShareService, ShareService>();
services.AddSingleton<IEmailService, EmailService>();
return services.BuildServiceProvider();
}
}
Here the Services property is initialized at startup, and all the application services and
viewmodels are registered. There is also a new Current property that can be used to
easily access the Services property from other views in the application. For instance:
C#
The key aspect here is that each service may very well be using platform-specific APIs,
but since those are all abstracted away through the interface our code is using, we don't
need to worry about them whenever we're just resolving an instance and using it to
perform operations.
Constructor injection
One powerful feature that is available is "constructor injection", which means that the DI
service provider is able to automatically resolve indirect dependencies between
registered services when creating instances of the type being requested. Consider the
following service:
C#
public class FileLogger : IFileLogger
{
private readonly IFilesService FileService;
private readonly IConsoleService ConsoleService;
public FileLogger(
IFilesService fileService,
IConsoleService consoleService)
{
FileService = fileService;
ConsoleService = consoleService;
}
Here we have a FileLogger type implementing the IFileLogger interface, and requiring
IFilesService and IConsoleService instances. Constructor injection means the DI
service provider will automatically gather all the necessary services, like so:
C#
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IFilesService, FilesService>();
services.AddSingleton<IConsoleService, ConsoleService>();
services.AddSingleton<IFileLogger, FileLogger>();
return services.BuildServiceProvider();
}
The DI service provider will automatically check whether all the necessary services are
registered, then it will retrieve them and invoke the constructor for the registered
IFileLogger concrete type, to get the instance to return.
C#
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Services
services.AddSingleton<IContactsService, ContactsService>();
services.AddSingleton<IPhoneService, PhoneService>();
// Viewmodels
services.AddTransient<ContactsViewModel>();
return services.BuildServiceProvider();
}
And then in our ContactsView , we would assign the data context as follows:
C#
public ContactsView()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}
More docs
For more info about Microsoft.Extensions.DependencyInjection , see here.
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
Messenger
Article • 04/07/2022
The IMessenger interface is a contract for types that can be used to exchange messages
between different objects. This can be useful to decouple different modules of an
application without having to keep strong references to types being referenced. It is also
possible to send messages to specific channels, uniquely identified by a token, and to
have different messengers in different sections of an application. The MVVM Toolkit
provides two implementations out of the box: WeakReferenceMessenger and
StrongReferenceMessenger: the former uses weak references internally, offering
automatic memory management for recipients, while the latter uses strong references
and requires developers to manually unsubscribe their recipients when they're no longer
needed (more details about how to unregister message handlers can be found below),
but in exchange for that offers better performance and far less memory usage.
How it works
Types implementing IMessenger are responsible for maintaining links between
recipients (receivers of messages) and their registered message types, with relative
message handlers. Any object can be registered as a recipient for a given message type
using a message handler, which will be invoked whenever the IMessenger instance is
used to send a message of that type. It is also possible to send messages through
specific communication channels (each identified by a unique token), so that multiple
modules can exchange messages of the same type without causing conflicts. Messages
sent without a token use the default shared channel.
There are two ways to perform message registration: either through the
IRecipient<TMessage> interface, or using a MessageHandler<TRecipient, TMessage>
delegate acting as message handler. The first lets you register all the handlers with a
single call to the RegisterAll extension, which automatically registers the recipients of
all the declared message handlers, while the latter is useful when you need more
flexibility or when you want to use a simple lambda expression as a message handler.
7 Note
Since the WeakReferenceMessenger type is simpler to use and matches the behavior
of the messenger type from the MvvmLight library, it is the default type being used
by the ObservableRecipient type in the MVVM Toolkit. The StrongReferenceType
can still be used, by passing an instance to the constructor of that class.
C#
// Create a message
public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
public LoggedInUserChangedMessage(User user) : base(user)
{
}
}
Let's imagine this message type being used in a simple messaging application, which
displays a header with the user name and profile image of the currently logged user, a
panel with a list of conversations, and another panel with messages from the current
conversation, if one is selected. Let's say these three sections are supported by the
HeaderViewModel , ConversationsListViewModel and ConversationViewModel types
The IMessenger instance takes care of delivering messages to all the registered
recipients. Note that a recipient can subscribe to messages of a specific type. Note that
inherited message types are not registered in the default IMessenger implementations
provided by the MVVM Toolkit.
When a recipient is not needed anymore, you should unregister it so that it will stop
receiving messages. You can unregister either by message type, by registration token, or
by recipient:
C#
2 Warning
meaning that unused recipients will still be eligible for garbage collection even
though they still have active message handlers. It is still good practice to
unsubscribe them though, to improve performances. On the other hand, the
StrongReferenceMessenger implementation uses strong references to track the
registered recipients. This is done for performance reasons, and it means that each
registered recipient should manually be unregistered to avoid memory leaks. That
is, as long as a recipient is registered, the StrongReferenceMessenger instance in use
will keep an active reference to it, which will prevent the garbage collector from
being able to collect that instance. You can either handle this manually, or you can
inherit from ObservableRecipient , which by default automatically takes care of
removing all the message registrations for recipient when it is deactivated (see docs
on ObservableRecipient for more info about this).
It is also possible to use the IRecipient<TMessage> interface to register message
handlers. In this case, each recipient will need to implement the interface for a given
message type, and provide a Receive(TMessage) method that will be invoke when
receiving messages, like so:
C#
// Create a message
public class MyRecipient : IRecipient<LoggedInUserChangedMessage>
{
public void Receive(LoggedInUserChangedMessage message)
{
// Handle the message here...
}
}
C#
// Create a message
public class LoggedInUserRequestMessage : RequestMessage<User>
{
}
The RequestMessage<T> class includes an implicit converter that makes the conversion
from a LoggedInUserRequestMessage to its contained User object possible. This will also
check that a response has been received for the message, and throw an exception if
that's not the case. It is also possible to send request messages without this mandatory
response guarantee: just store the returned message in a local variable, and then
manually check whether a response value is available or not. Doing so will not trigger
the automatic exception if a response is not received when the Send method returns.
The same namespace also includes base requests message for other scenarios:
AsyncRequestMessage<T>, CollectionRequestMessage<T> and
AsyncCollectionRequestMessage<T>. Here's how you can use an async request
message:
C#
// Create a message
public class LoggedInUserRequestMessage : AsyncRequestMessage<User>
{
}
// Request the value from another module (we can directly await on the
request)
User user = await
WeakReferenceMessenger.Default.Send<LoggedInUserRequestMessage>();
Examples
Check out the sample app (for multiple UI frameworks) to see the MVVM Toolkit
in action.
You can also find more examples in the unit tests .
Putting things together
Article • 02/07/2023
Now that we've outlined all the different components that are available through the
CommunityToolkit.Mvvm package, we can look at a practical example of them all coming
together to build a single, larger example. In this case, we want to build a very simple
and minimalistic Reddit browser for a select number of subreddits.
A minimal Reddit browser made up of two "widgets": one showing posts from a
subreddit, and the other one showing the currently selected post. The two widget
need to be self contained and without strong references to one another.
We want users to be able to select a subreddit from a list of available options, and
we want to save the selected subreddit as a setting and load it up the next time
the sample is loaded.
We want the subreddit widget to also offer a refresh button to reload the current
subreddit.
For the purposes of this sample, we don't need to be able to handle all the
possible post types. We'll just assign a sample text to all loaded posts and display
that directly, to make things simpler.
Commands: we need the view to be able to request the viewmodel to reload the
current list of posts from the selected subreddit. We can use the
AsyncRelayCommand type to wrap a private method that will fetch the posts from
Reddit. Here we're exposing the command through the IAsyncRelayCommand
interface, to avoid strong references to the exact command type we're using. This
will also allow us to potentially change the command type in the future without
having to worry about any UI component relying on that specific type being used.
Properties: we need to expose a number of values to the UI, which we can do with
either observable properties if they're values we intend to completely replace, or
with properties that are themselves observable (eg. ObservableCollection<T> ). In
this case, we have:
ObservableCollection<object> Posts , which is the observable list of loaded
the subreddits that we allow users to choose from. This property is never
updated, so it doesn't need to be observable either.
string SelectedSubreddit , which is the currently selected subreddit. This
property needs to be bound to the UI, as it'll be used both to indicate the last
selected subreddit when the sample is loaded, and to be manipulated directly
from the UI as the user changes the selection. Here we're using the SetProperty
method from the ObservableObject class.
object SelectedPost , which is the currently selected post. In this case we're
using the SetProperty method from the ObservableRecipient class to indicate
that we also want to broadcast notifications when this property changes. This is
done to be able to notify the post widget that the current post selection is
changed.
Methods: we just need a private LoadPostsAsync method which will be wrapped by
our async command, and which will contain the logic to load posts from the
selected subreddit.
C#
/// <summary>
/// Gets the <see cref="IAsyncRelayCommand"/> instance responsible for
loading posts.
/// </summary>
public IAsyncRelayCommand LoadPostsCommand { get; }
/// <summary>
/// Gets the collection of loaded posts.
/// </summary>
public ObservableCollection<object> Posts { get; } = new
ObservableCollection<object>();
/// <summary>
/// Gets the collection of available subreddits to pick from.
/// </summary>
public IReadOnlyList<string> Subreddits { get; } = new[]
{
"microsoft",
"windows",
"surface",
"windowsphone",
"dotnet",
"csharp"
};
/// <summary>
/// Gets or sets the currently selected subreddit.
/// </summary>
public string SelectedSubreddit
{
get => selectedSubreddit;
set => SetProperty(ref selectedSubreddit, value);
}
/// <summary>
/// Gets or sets the currently selected subreddit.
/// </summary>
public object SelectedPost
{
get => selectedPost;
set => SetProperty(ref selectedPost, value, true);
}
/// <summary>
/// Loads the posts from a specified subreddit.
/// </summary>
private async Task LoadPostsAsync()
{
// TODO...
}
}
Now let's take a look at what we need for viewmodel of the post widget. This will be a
much simpler viewmodel, as it really only needs to expose a Post property with the
currently selected post, and to receive broadcast messages from the subreddit widget to
update the Post property. It can look something like this:
C#
public sealed class PostWidgetViewModel : ObservableRecipient,
IRecipient<PropertyChangedMessage<object>>
{
private object post;
/// <summary>
/// Gets the currently selected post, if any.
/// </summary>
public object Post
{
get => post;
private set => SetProperty(ref post, value);
}
/// <inheritdoc/>
public void Receive(PropertyChangedMessage<object> message)
{
if (message.Sender.GetType() == typeof(SubredditWidgetViewModel) &&
message.PropertyName ==
nameof(SubredditWidgetViewModel.SelectedPost))
{
Post = message.NewValue;
}
}
}
In this case, we're using the IRecipient<TMessage> interface to declare the messages we
want our viewmodel to receive. The handlers for the declared messages will be added
automatically by the ObservableRecipient class when the IsActive property is set to
true . Note that it is not mandatory to use this approach, and manually registering each
C#
/// <inheritdoc/>
public void Receive(PropertyChangedMessage<object> message)
{
if (message.Sender.GetType() == typeof(SubredditWidgetViewModel) &&
message.PropertyName ==
nameof(SubredditWidgetViewModel.SelectedPost))
{
Post = message.NewValue;
}
}
}
We now have a draft of our viewmodels ready, and we can start looking into the services
we need.
7 Note
The sample is built using the dependency injection pattern, which is the
recommended approach to deal with services in viewmodels. It is also possible to
use other patterns, such as the service locator pattern, but the MVVM Toolkit does
not offer built-in APIs to enable that.
Since we want some of our properties to be saved and persisted, we need a way for
viewmodels to be able to interact with the application settings. We shouldn't use
platform-specific APIs directly in our viewmodels though, as that would prevent us from
having all our viewmodels in a portable, .NET Standard project. We can solve this issue
by using services, and the APIs in the Microsoft.Extensions.DependencyInjection library
to setup our IServiceProvider instance for the application. The idea is to write
interfaces that represent all the API surface that we need, and then to implement
platform-specific types implementing this interface on all our application targets. The
viewmodels will only interact with the interfaces, so they will not have any strong
reference to any platform-specific type at all.
C#
/// <summary>
/// Reads a value from the current <see cref="IServiceProvider"/>
instance and returns its casting in the right type.
/// </summary>
/// <typeparam name="T">The type of the object to retrieve.</typeparam>
/// <param name="key">The key associated to the requested object.
</param>
[Pure]
T GetValue<T>(string key);
}
We can assume that platform-specific types implementing this interface will take care of
dealing with all the logic necessary to actually serialize the settings, store them to disk
and then read them back. We can now use this service in our SubredditWidgetViewModel ,
in order to make the SelectedSubreddit property persistent:
C#
/// <summary>
/// Gets the <see cref="ISettingsService"/> instance to use.
/// </summary>
private readonly ISettingsService SettingsService;
/// <summary>
/// Creates a new <see cref="SubredditWidgetViewModel"/> instance.
/// </summary>
public SubredditWidgetViewModel(ISettingsService settingsService)
{
SettingsService = settingsService;
selectedSubreddit = settingsService.GetValue<string>
(nameof(SelectedSubreddit)) ?? Subreddits[0];
}
/// <summary>
/// Gets or sets the currently selected subreddit.
/// </summary>
public string SelectedSubreddit
{
get => selectedSubreddit;
set
{
SetProperty(ref selectedSubreddit, value);
SettingsService.SetValue(nameof(SelectedSubreddit), value);
}
}
Here we're using dependency injection and constructor injection, as mentioned above.
We've declared an ISettingsService SettingsService field that just stores our settings
service (which we're receiving as parameter in the viewmodel constructor), and then
we're initializing the SelectedSubreddit property in the constructor, by either using the
previous value or just the first available subreddit. Then we also modified the
SelectedSubreddit setter, so that it will also use the settings service to save the new
value to disk.
Great! Now we just need to write a platform specific version of this service, this time
directly inside one of our app projects. Here's what that service might look like on UWP:
C#
/// <inheritdoc/>
public void SetValue<T>(string key, T value)
{
if (!SettingsStorage.ContainsKey(key)) SettingsStorage.Add(key,
value);
else SettingsStorage[key] = value;
}
/// <inheritdoc/>
public T GetValue<T>(string key)
{
if (SettingsStorage.TryGetValue(key, out object value))
{
return (T)value;
}
return default;
}
}
The final piece of the puzzle is to inject this platform-specific service into our service
provider instance. We can do this at startup, like so:
C#
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application
services.
/// </summary>
public IServiceProvider Services { get; }
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<ISettingsService, SettingsService>();
services.AddTransient<PostWidgetViewModel>();
return services.BuildServiceProvider();
}
service, meaning every time we retrieve an instance, it will be a new one (you can
imagine this being useful if wanted to have multiple, independent post widgets). This
means that every time we resolve an ISettingsService instance while the app in use is
the UWP one, it will receive a SettingsService instance, which will use the UWP APIs
behind the scene to manipulate settings. Perfect!
C#
That PostsQueryResponse is a model we wrote that maps the JSON response for that API.
The exact structure of that class is not important - suffice to say that it contains a
collection of Post items, which are simple models representing our posts, like this:
C#
/// <summary>
/// Gets or sets the URL to the post thumbnail, if present.
/// </summary>
public string Thumbnail { get; set; }
/// <summary>
/// Gets the text of the post.
/// </summary>
public string SelfText { get; }
}
Once we have our service and our models, we can plug them into our viewmodels to
complete our backend. While doing so, we can also replace those object placeholders
with the Post type we've defined:
C#
/// <summary>
/// Loads the posts from a specified subreddit.
/// </summary>
private async Task LoadPostsAsync()
{
var response = await
RedditService.GetSubredditPostsAsync(SelectedSubreddit);
Posts.Clear();
The last missing piece now is just to inject the actual service into our service provider.
The big difference in this case is that by using refit we don't actually need to
implement the service at all! The library will automatically create a type implementing
the service for us, behind the scenes. So we only need to get an IRedditService
instance and inject it directly, like so:
C#
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<ISettingsService, SettingsService>();
services.AddSingleton(RestService.For<IRedditService>
("https://www.reddit.com/"));
services.AddTransient<PostWidgetViewModel>();
return services.BuildServiceProvider();
}
And that's all we need to do! We now have all our backend ready to use, including two
custom services that we created specifically for this app! 🎉
Building the UI
Now that all the backend is completed, we can write the UI for our widgets. Note how
using the MVVM pattern let us focus exclusively on the business logic at first, without
having to write any UI-related code until now. Here we'll remove all the UI code that's
not interacting with our viewmodels, for simplicity, and we'll go through each different
control one by one. The full source code can be found in the sample app.
Before going through the various controls, here's how we can resolve viewmodels for all
the different views in our application (eg. the PostWidgetView ):
C#
public PostWidgetView()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<PostWidgetViewModel>
();
}
Let's start with the subreddit widget, which features a ComboBox to select a subreddit, a
Button to refresh the feed, a ListView to display posts and a ProgressBar to indicate
when the feed is loading. We'll assume that the ViewModel property represents an
instance of the viewmodel we've described before - this can be declared either in XAML
or directly in code behind.
Subreddit selector:
XML
<ComboBox
ItemsSource="{x:Bind ViewModel.Subreddits}"
SelectedItem="{x:Bind ViewModel.SelectedSubreddit, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="SelectionChanged">
<core:InvokeCommandAction Command="{x:Bind
ViewModel.LoadPostsCommand}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ComboBox>
Here we're binding the source to the Subreddits property, and the selected item to the
SelectedSubreddit property. Note how the Subreddits property is only bound once, as
the collection itself sends change notifications, while the SelectedSubreddit property is
bound with the TwoWay mode, as we need it both to be able to load the value we
retrieve from our settings, as well as updating the property in the viewmodel when the
user changes the selection. Additionally, we're using a XAML behavior to invoke our
command whenever the selection changes.
Refresh button:
XML
This component is extremely simple, we're just binding our custom command to the
Command property of the button, so that the command will be invoked whenever the user
clicks on it.
Posts list:
XML
<ListView
ItemsSource="{x:Bind ViewModel.Posts}"
SelectedItem="{x:Bind ViewModel.SelectedPost, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Post">
<Grid>
<TextBlock Text="{x:Bind Title}"/>
<controls:ImageEx Source="{x:Bind Thumbnail}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here we have a ListView binding the source and selection to our viewmodel property,
and also a template used to display each post that is available. We're using x:DataType
to enable x:Bind in our template, and we have two controls binding directly to the
Title and Thumbnail properties of our post.
Loading bar:
XML
Here we're binding to the IsRunning property, which is part of the IAsyncRelayCommand
interface. The AsyncRelayCommand type will take care of raising notifications for that
property whenever the asynchronous operation starts or completes for that command.
The last missing piece is the UI for the post widget. As before, we've removed all the UI-
related code that was not necessary to interact with the viewmodels, for simplicity. The
full source code is available in the sample app.
XML
<Grid>
<!--Header-->
<Grid>
<TextBlock Text="{x:Bind ViewModel.Post.Title, Mode=OneWay}"/>
<controls:ImageEx Source="{x:Bind ViewModel.Post.Thumbnail,
Mode=OneWay}"/>
</Grid>
<!--Content-->
<ScrollViewer>
<TextBlock Text="{x:Bind ViewModel.Post.SelfText, Mode=OneWay}"/>
</ScrollViewer>
</Grid>
Here we just have a header, with a TextBlock and an ImageEx control binding their Text
and Source properties to the respective properties in our Post model, and a simple
TextBlock inside a ScrollViewer that is used to display the (sample) content of the
selected post.
Sample Application
Sample application available here .
Good to go! 🚀
We've now built all our viewmodels, the necessary services, and the UI for our widgets -
our simple Reddit browser is completed! This was just meant to be an example of how
to build an app following the MVVM pattern and using the APIs from the MVVM Toolkit.
As stated above, this is only a reference, and you're free to modify this structure to fit
your needs and/or to pick and choose only a subset of components from the library.
Regardless of the approach you take, the MVVM Toolkit should provide a solid
foundation to hit the ground running when starting a new application, by letting you
focus on your business logic instead of having to worry about manually doing all the
necessary plumbing to enable proper support for the MVVM pattern.
Migrating from MvvmLight
Article • 01/18/2023
This article outlines some of the key differences between the MvvmLight Toolkit and
the MVVM Toolkit to ease your migration.
While this article specifically focuses on the migrations from MvvmLight to the MVVM
Toolkit, note that there are additional improvements that have been made within the
MVVM Toolkit, so it is highly recommend taking a look at the documentation for the
individual new APIs.
Migrating ObservableObject
The following steps focus on migrating your existing components which take advantage
of the ObservableObject of the MvvmLight Toolkit. The MVVM Toolkit provides an
ObservableObject type that is similar.
The first change here will be swapping using directives in your components.
C#
// MvvmLight
using GalaSoft.MvvmLight;
// MVVM Toolkit
using CommunityToolkit.Mvvm.ComponentModel;
Below are a list of migrations that will need to be performed if being used in your
current solution.
ObservableObject methods
Set<T>(Expression, ref T, T)
C#
// MvvmLight
Set(() => MyProperty, ref this.myProperty, value);
// MVVM Toolkit
SetProperty(ref this.myProperty, value);
Note that the string parameter is not required if the method is being called from the
property's setter as it is inferred from the caller member name, as can be seen here. If
you want to invoke SetProperty for a property that is different from the one where the
method is being invoked, you can do so by using the nameof operator, which can be
useful to make the code less error prone by not having hardcoded names. For instance:
C#
Set<T>(string, ref T, T)
C#
// MvvmLight
Set(nameof(MyProperty), ref this.myProperty, value);
// MVVM Toolkit
SetProperty(ref this.myProperty, value);
Set<T>(ref T, T, string)
C#
// MvvmLight
Set(ref this.myProperty, value, nameof(MyProperty));
// MVVM Toolkit
SetProperty(ref this.myProperty, value);
RaisePropertyChanged(string)
OnPropertyChanged(string) .
C#
// MvvmLight
RaisePropertyChanged(nameof(MyProperty));
// MVVM Toolkit
OnPropertyChanged();
As with SetProperty , the name of the current property is automatically inferred by the
OnPropertyChanged method. If you want to use this method to manually raise the
PropertyChanged event for another property, you can also manually specify the name of
that property by using the nameof operator again. For instance:
C#
OnPropertyChanged(nameof(SomeProperty));
RaisePropertyChanged<T>(Expression)
C#
// MvvmLight
RaisePropertyChanged(() => MyProperty);
// MVVM Toolkit
OnPropertyChanged(nameof(MyProperty));
VerifyPropertyName(string)
The reason for the omission from the MVVM Toolkit is that using the nameof keyword
for a property verifies that it exists. When MvvmLight was built, the nameof keyword was
not available and this method was used to ensure that the property existed on the
object.
C#
// MvvmLight
VerifyPropertyName(nameof(MyProperty));
// MVVM Toolkit
// No direct replacement, remove
ObservableObject properties
PropertyChangedHandler
PropertyChangedHandler does not have a direct replacement.
To raise a property changed event via the PropertyChanged event handler, you need to
call the OnPropertyChanged method instead.
C#
// MvvmLight
PropertyChangedEventHandler handler = PropertyChangedHandler;
// MVVM Toolkit
OnPropertyChanged();
Migrating ViewModelBase
The following steps focus on migrating your existing components which take advantage
of the ViewModelBase of the MvvmLight Toolkit.
Below are a list of migrations that will need to be performed if being used in your
current solution.
ViewModelBase methods
replacement.
C#
// MvvmLight
Set(nameof(MyProperty), ref this.myProperty, value, true);
// MVVM Toolkit
SetProperty(ref this.myProperty, value, true);
Note, the value and broadcast boolean parameters are not optional in the MVVM
Toolkit's implementation and must be provided to use this method. The reason for this
change is that by omitting the broadcast parameter when calling this method, it will by
default call the ObservableObject's SetProperty method.
Also, the string parameter is not required if the method is being called from the
property's setter as it is inferred from the caller member name, just like with the
methods in the base ObservableObject class.
C#
// MvvmLight
Set(ref this.myProperty, value, true, nameof(MyProperty));
// MVVM Toolkit
SetProperty(ref this.myProperty, value, true);
It is recommended for improved performance that you replace this with the MVVM
Toolkit's SetProperty<T>(ref T, T, bool, string) using the nameof keyword instead.
C#
// MvvmLight
Set<MyObject>(() => MyProperty, ref this.myProperty, value, true);
// MVVM Toolkit
SetProperty(ref this.myProperty, value, true);
Broadcast<T>(T, T, string)
C#
// MvvmLight
Broadcast<MyObject>(oldValue, newValue, nameof(MyProperty));
// MVVM Toolkit
Broadcast(oldValue, newValue, nameof(MyProperty));
Note, the message sent via the Messenger property when calling the Broadcast method
has a direct replacement for PropertyChangedMessage within the MVVM Toolkit library.
RaisePropertyChanged<T>(string, T, T, bool)
C#
// MvvmLight
RaisePropertyChanged<MyObject>(nameof(MyProperty), oldValue, newValue,
true);
// MVVM Toolkit
OnPropertyChanged();
Broadcast(oldValue, newValue, nameof(MyProperty));
RaisePropertyChanged<T>(Expression, T, T, bool)
C#
// MvvmLight
RaisePropertyChanged<MyObject>(() => MyProperty, oldValue, newValue, true);
// MVVM Toolkit
OnPropertyChanged(nameof(MyProperty));
Broadcast(oldValue, newValue, nameof(MyProperty));
ICleanup.Cleanup()
OnDeactivated in the MVVM Toolkit will also unregister all of the registered messenger
C#
// MvvmLight
Cleanup();
// MVVM Toolkit
OnDeactivated();
Note, the OnActivated and OnDeactivated methods can be called from your existing
solution as with Cleanup .
However, the ObservableRecipient exposes an IsActive property that also controls the
call to these methods when it is set.
ViewModelBase properties
MessengerInstance
C#
// MvvmLight
IMessenger messenger = MessengerInstance;
// MVVM Toolkit
IMessenger messenger = Messenger;
7 Note
IsInDesignMode
There is no direct replacement for the IsInDesignMode property and any code using this
should be altered or removed.
The reason for the omission from the MVVM Toolkit is that the IsInDesignMode property
exposed platform-specific implementations. The MVVM Toolkit has been designed to be
platform agnostic.
C#
// MvvmLight
var isInDesignMode = IsInDesignMode;
// MVVM Toolkit
// No direct replacement, remove
IsInDesignModeStatic
There is no direct replacement for the IsInDesignModeStatic property and any code
using this should be altered or removed.
The reason for the omission from the MVVM Toolkit is that the IsInDesignMode property
exposed platform-specific implementations. The MVVM Toolkit has been designed to be
platform agnostic.
C#
// MvvmLight
var isInDesignMode = ViewModelBase.IsInDesignModeStatic;
// MVVM Toolkit
// No direct replacement, remove
Migrating RelayCommand
The following steps focus on migrating your existing components which take advantage
of the RelayCommand of the MvvmLight Toolkit.
Below are a list of migrations that will need to be performed if being used in your
current solution. Where a method or property isn't listed, there is a direct replacement
with the same name in the MVVM Toolkit and there is no change required.
The first change here will be swapping using directives in your components.
C#
// MvvmLight
using GalaSoft.MvvmLight.Command;
using Galasoft.MvvmLight.CommandWpf;
// MVVM Toolkit
using CommunityToolkit.Mvvm.Input;
7 Note
MvvmLight uses weak references to establish the link between the command and
the action called from the associated class. This is not required by the MVVM
Toolkit implementation and if this optional parameter has been set to true in any
of your constructors, this will be removed.
You can simply replace your existing RelayCommand with the AsyncRelayCommand which
has been built for asynchronous purposes.
C#
// MvvmLight
var command = new RelayCommand(() => OnCommandAsync());
var command = new RelayCommand(async () => await OnCommandAsync());
// MVVM Toolkit
var asyncCommand = new AsyncRelayCommand(OnCommandAsync);
RelayCommand methods
RaiseCanExecuteChanged()
C#
// MvvmLight
var command = new RelayCommand(OnCommand);
command.RaiseCanExecuteChanged();
// MVVM Toolkit
var command = new RelayCommand(OnCommand);
command.NotifyCanExecuteChanged();
Migrating RelayCommand<T>
The following steps focus on migrating your existing components which take advantage
of the RelayCommand<T> of the MvvmLight Toolkit.
Below are a list of migrations that will need to be performed if being used in your
current solution. Where a method or property isn't listed, there is a direct replacement
with the same name in the MVVM Toolkit and there is no change required.
The first change here will be swapping using directives in your components.
C#
// MvvmLight
using GalaSoft.MvvmLight.Command;
using Galasoft.MvvmLight.CommandWpf;
// MVVM Toolkit
using CommunityToolkit.Mvvm.Input;
Using RelayCommand with asynchronous actions
If you are currently using the MvvmLight RelayCommand<T> implementation with
asynchronous actions, the MVVM Toolkit exposes an improved implementation for these
scenarios.
You can simply replace your existing RelayCommand<T> with the AsyncRelayCommand<T>
which has been built for asynchronous purposes.
C#
// MvvmLight
var command = new RelayCommand<string>(async () => await OnCommandAsync());
// MVVM Toolkit
var asyncCommand = new AsyncRelayCommand<string>(OnCommandAsync);
RelayCommand<T> Methods
RaiseCanExecuteChanged()
C#
// MvvmLight
var command = new RelayCommand<string>(OnCommand);
command.RaiseCanExecuteChanged();
// MVVM Toolkit
var command = new RelayCommand<string>(OnCommand);
command.NotifyCanExecuteChanged();
Migrating SimpleIoc
The IoC implementation in the MVVM Toolkit doesn't include any built-in logic to
handle dependency injection on its own, so you're free to use any 3rd party library to
retrieve an IServiceProvider instance that you can then pass to the
Ioc.ConfigureServices method. In the examples below, the ServiceCollection type
from the Microsoft.Extensions.DependencyInjection library will be used.
This is the biggest change between MvvmLight and the MVVM Toolkit.
This implementation will feel familiar if you've implemented dependency injection with
ASP.NET Core applications.
C#
With the MVVM Toolkit, you would achieve the same as follows.
C#
Resolving dependencies
Once initialized, services can be retrieved from the Ioc class just like with SimpleIoc :
C#
Migrating to the MVVM Toolkit, you will achieve the same with:
C#
C#
SimpleIoc.Default.Unregister<INavigationService>();
There is no direct replacement for removing dependencies with the MVVM Toolkit Ioc
implementation.
Preferred constructor
When registering your dependencies with MvvmLight's SimpleIoc , you have the option
in your classes to provide a PreferredConstructor attribute for those with multiple
constructors.
This attribute will need removing where used, and you will need to use any attributes
from the 3rd party dependency injection library in use, if supported.
Migrating Messenger
The following steps focus on migrating your existing components which take advantage
of the Messenger of the MvvmLight Toolkit.
Below are a list of migrations that will need to be performed if being used in your
current solution.
The first change here will be swapping using directives in your components.
C#
// MvvmLight
using GalaSoft.MvvmLight.Messaging;
// MVVM Toolkit
using CommunityToolkit.Mvvm.Messaging;
Messenger methods
Register<TMessage>(object, Action<TMessage>)
C#
// MvvmLight
Messenger.Default.Register<MyMessage>(this, this.OnMyMessageReceived);
// MVVM Toolkit
Messenger.Register<MyViewModel, MyMessage>(this, static (r, m) =>
r.OnMyMessageReceived(m));
The reason for this signature is that it allows the messenger to use weak references to
properly track recipients and to avoid creating closures to capture the recipient itself.
That is, the input recipient is passed as an input to the lambda expression, so it doesn't
need to be captured by the lambda expression itself. This also results in more efficient
code, as the same handler can be reused multiple times with no allocations. Note that
this is just one of the supported ways to register handlers, and it is possible to also use
the IRecipient<TMessage> interface instead (detailed in the messenger docs), which
makes the registration automatic and less verbose.
7 Note
Additionally, this example and the ones below will just be using the Messenger property
from ObservableRecipient . If you want to just statically access a messenger instance
from anywhere else in your code, the same examples apply as well, with the only
difference being that Messenger needs to be replaced with eg.
WeakReferenceMessenger.Default instead.
Register<TMessage>(object, bool, Action<TMessage>)
There is no direct replacement for this registration mechanism which allows you to
support receiving messages for derived message types also. This change is intentional as
the Messenger implementation aims to not use reflection to achieve its performance
benefits.
Alternatively, there are a few options that can be done to achieve this functionality.
C#
// MvvmLight
Messenger.Default.Register<MyMessage>(this, true, this.OnMyMessageReceived);
// MVVM Toolkit
Messenger.Register<MyViewModel, MyMessage>(this, static (r, m) =>
r.OnMyMessageReceived(m));
Messenger.Register<MyViewModel, MyOtherMessage>(this, static (r, m) =>
r.OnMyMessageReceived(m));
C#
// MvvmLight
Messenger.Default.Register<MyMessage>(this, nameof(MyViewModel),
this.OnMyMessageReceived);
// MVVM Toolkit
Messenger.Register<MyViewModel, MyMessage, string>(this,
nameof(MyViewModel), static (r, m) => r.OnMyMessageReceived(m));
There is no direct replacement for this registration mechanism which allows you to
support receiving messages for derived message types also. This change is intentional as
the Messenger implementation aims to not use reflection to achieve its performance
benefits.
Alternatively, there are a few options that can be done to achieve this functionality.
C#
// MvvmLight
Messenger.Default.Register<MyMessage>(this, nameof(MyViewModel), true,
this.OnMyMessageReceived);
// MVVM Toolkit
Messenger.Register<MyViewModel, MyMessage, string>(this,
nameof(MyViewModel), static (r, m) => r.OnMyMessageReceived(m));
Messenger.Register<MyViewModel, MyOtherMessage, string>(this,
nameof(MyViewModel), static (r, m) => r.OnMyMessageReceived(m));
Send<TMessage>(TMessage)
C#
// MvvmLight
Messenger.Default.Send<MyMessage>(new MyMessage());
Messenger.Default.Send(new MyMessage());
// MVVM Toolkit
Messenger.Send(new MyMessage());
In the above scenario where the message being sent has a parameterless constructor,
the MVVM Toolkit has a simplified extension to send a message in this format.
C#
// MVVM Toolkit
Messenger.Send<MyMessage>();
Send<TMessage>(TMessage, object)
The functionality of Send<TMessage>(TMessage, object) can be achieved with the MVVM
Toolkit's Send<TMessage, TToken>(TMessage, TToken) method.
C#
// MvvmLight
Messenger.Default.Send<MyMessage>(new MyMessage(), nameof(MyViewModel));
Messenger.Default.Send(new MyMessage(), nameof(MyViewModel));
// MVVM Toolkit
Messenger.Send(new MyMessage(), nameof(MyViewModel));
Unregister(object)
C#
// MvvmLight
Messenger.Default.Unregister(this);
// MVVM Toolkit
Messenger.UnregisterAll(this);
Unregister<TMessage>(object)
C#
// MvvmLight
Messenger.Default.Unregister<MyMessage>(this);
// MVVM Toolkit
Messenger.Unregister<MyMessage>(this);
Unregister<TMessage>(object, Action<TMessage>)
C#
// MvvmLight
Messenger.Default.Unregister<MyMessage>(this, OnMyMessageReceived);
// MVVM Toolkit
Messenger.Unregister<MyMessage>(this);
Unregister<TMessage>(object, object)
C#
// MvvmLight
Messenger.Default.Unregister<MyMessage>(this, nameof(MyViewModel));
// MVVM Toolkit
Messenger.Unregister<MyMessage, string>(this, nameof(MyViewModel));
The reason for the omission is that a message recipient can only have a single registered
handler for any given message type.
C#
// MvvmLight
Messenger.Default.Unregister<MyMessage>(this, nameof(MyViewModel),
OnMyMessageReceived);
// MVVM Toolkit
Messenger.Unregister<MyMessage, string>(this, nameof(MyViewModel));
Cleanup()
The Cleanup method has a direct replacement with the same name in the MVVM Toolkit.
Note that this method is only useful when a messenger using weak references is being
used, while the StrongReferenceMessenger type will simply do nothing when this method
is called, as the internal state is already trimmed automatically as the messenger is being
used.
C#
// MvvmLight
Messenger.Default.Cleanup();
// MVVM Toolkit
Messenger.Cleanup();
RequestCleanup()
There is no direct replacement for the RequestCleanup method in the MVVM Toolkit. In
the context of MvvmLight, RequestCleanup is used to initiate a request to remove
registrations which are no longer alive as the implementation takes advantage of weak
references.
Any calls to the RequestCleanup method can be removed or replaced with Cleanup .
C#
// MvvmLight
Messenger.Default.RequestCleanup();
// MVVM Toolkit
// No direct replacement, remove
ResetAll()
The functionality of ResetAll() can be achieved with the MVVM Toolkit's Reset()
method.
Unlike MvvmLight's implementation which nulls out the instance, the MVVM Toolkit
clears the registered maps.
C#
// MvvmLight
Messenger.Default.ResetAll();
// MVVM Toolkit
Messenger.Reset();
OverrideDefault(IMessenger)
C#
// MvvmLight
Messenger.OverrideDefault(new Messenger());
// MVVM Toolkit
// No direct replacement
Reset()
There is no direct replacement for the static Reset method in the MVVM Toolkit.
The same functionality can be achieved by calling the Reset method of the static
Default instance of one of the messenger types.
C#
// MvvmLight
Messenger.Reset();
// MVVM Toolkit
WeakReferenceMessenger.Default.Reset();
Messenger static properties
Default
implementation.
C#
// MvvmLight
IMessenger messenger = Messenger.Default;
// MVVM Toolkit
IMessenger messenger = WeakReferenceMessenger.Default;
While the MVVM Toolkit provides some alternatives, there are no direct replacement for
these message types. We recommend looking at our available message types .
Alternatively, if your solution takes advantage of the MvvmLight message types, these
can easily be ported into your own codebase.
The following components and their associated helpers/extension methods do not have
a replacement and will need considering when migrating to the MVVM Toolkit.
Android/iOS/Windows specific
DialogService
DispatcherHelper
NavigationService
Android/iOS specific
ActivityBase
Binding
BindingMode
PropertyChangedEventManager
UpdateTriggerMode
Android specific
CachingViewHolder
ObservableAdapter
ObservableRecyclerAdapter
iOS specific
ObservableCollectionViewSource
ObservableTableViewController
ObservableTableViewSource
Helpers
Empty
WeakAction
WeakFunc
Migrating from MVVM Basic
Article • 01/18/2023
This article explains how to migrate apps built with the MVVM Basic option in
Windows Template Studio to use the MVVM Toolkit library instead. It applies to both
UWP and WPF apps created with Windows Template Studio.
This article focuses exclusively on migration and does not cover how to use the
additional functionality that the library provides.
Updating a project
There are four steps to migrate the code generated by Windows Template Studio.
C#
\Helpers\Observable.cs
\Helpers\RelayCommand.cs
If you try and build the project at this point you will see lots of errors. These can be
useful for identifying files that require changes.
Change all classes that previously inherited from Observable to inherit from
ObservableObject .
For example
C#
will become
C#
You can either add the appropriate directive manually, of move the cursor to the
ObservableObject and press Ctrl+. to access the Quick Action menu to add this for
you.
C#
using CommunityToolkit.Mvvm.ComponentModel;
You can either add the appropriate directive manually, of move the cursor to the
RelayCommand and press Ctrl+. to access the Quick Action menu to add this for you.
C#
using CommunityToolkit.Mvvm.Input;
So,
C#
will become
C#
So,
C#
(UndoCommand as RelayCommand)?.OnCanExecuteChanged();
will become
C#
(UndoCommand as RelayCommand)?.NotifyCanExecuteChanged();
The app should now work with the same functionality as before.