We’re happy to announce that the first preview of the upcoming .NET Community Toolkit 8.0 release is now live, bringing with it lots of new features, improvements and bug fixes! As with every release, all changes were influenced by feedback received both by teams here at Microsoft using the Toolkit as well as other developers in our community 🎉
This is also our first release from the brand new .NET Community Toolkit repository, which will be the home of all our .NET libraries going forward. Libraries in this new toolkit have no dependencies on any specific UI frameworks, so they can be used by all developers regardless of the framework or runtime they choose to work on their projects. That is, the .NET Community Toolkit can be referenced by applications and libraries targeting .NET, .NET Core, .NET Standard, .NET Framework, WPF, UWP, MAUI, Xamarin, Unity, and more!.
These libraries include:
- CommunityToolkit.Common
- CommunityToolkit.Mvvm (aka “Microsoft MVVM Toolkit”)
- CommunityToolkit.Diagnostics
- CommunityToolkit.HighPerformance
NOTE: the docs links are still in the process of migrating to the new “CommunityToolkit” directory, and the API references still refer to the previous “Microsoft.Toolkit” namespace for now. Refer to our previous blog post for more info about this change in namespace and package identity, and this other blog post to learn more about the naming change.
Here is a breakdown of the main changes you can expect in this new release.
🤖 MVVM Toolkit source generators
This new release of the MVVM Toolkit (previously introduced in our 7.0 release) includes a revamped and improved preview of our source generators. All generators have been rewritten from scratch to be incremental generators, meaning they will run much faster than before and they will help keep the IDE fast and responsive even when working on large scale projects. These APIs are meant to greatly reduce the necessarily boilerplate when working with MVVM, with helpers ranging from observable properties to commands. Here is a breakdown of all included features.
Commands
Creating commands can now be much simpler than before, by using the new [ICommand]
attribute. This will let the MVVM Toolkit automatically generate commands (using the RelayCommand
types included in the library) with the right signature, depending on the annotated method.
This is an example of how one would usually setup a command:
private IRelayCommand<User> greetUserCommand;
public IRelayCommand<User> GreetUserCommand => greetUserCommand ??= new RelayCommand<User>(GreetUser);
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
This can now be simplified to just this:
[ICommand]
private void GreetUser(User user)
{
Console.WriteLine($"Hello {user.Name}!");
}
The source generator will take care of creating the right GreetUserCommand
property based on the annotated method. Additionally, a CanExecute
method can also be specified, and it is also possible to control the concurrency level for asynchronous commands. That is, if an asynchronous method is used for a command, it is now possible to just set AllowConcurrentExecutions = false
to make it so that the generated command won’t be allowed to run more than once concurrently.
Observable properties
Writing observable properties can feel a bit of a chore at times, especially when also having to add additional logic to handle dependent properties being notifed. Now, all of this can be greatly simplified by using the new attributes from the MVVM Toolkit, and letting the source generator create observable properties behind the scenes. The new attributes are [ObservableProperty]
, [AlsoNotifyChangeFor]
and [AlsoNotifyCanExecuteFor]
.
Consider a scenario where there are two observable properties, a dependent property and the command that was defined above, and where both the dependent property and the command need to be notified when any of the two observable properties change. That is, whenever either FirstName
or LastName
change, FullName
is also notified, as well as the GreetUserCommand
.
This is how it would have been done in the past:
private string? firstName;
public string? FirstName
{
get => firstName;
set
{
if (SetProperty(ref firstName, value))
{
OnPropertyChanged(nameof(FullName));
GreetUserCommand.NotifyCanExecuteChanged();
}
}
}
private string? lastName;
public string? LastName
{
get => lastName;
set
{
if (SetProperty(ref lastName, value))
{
OnPropertyChanged(nameof(FullName));
GreetUserCommand.NotifyCanExecuteChanged();
}
}
}
public string? FullName => $"{FirstName} {LastName}";
This can now all be rewritten as follows instead:
[ObservableProperty]
[AlsoNotifyChangeFor(nameof(FullName))]
[AlsoNotifyCanExecuteFor(nameof(GreetUserCommand))]
private string? firstName;
[ObservableProperty]
[AlsoNotifyChangeFor(nameof(FullName))]
[AlsoNotifyCanExecuteFor(nameof(GreetUserCommand))]
private string? lastName;
public string? FullName => $"{FirstName} {LastName}";
The MVVM Toolkit will handle code generation for those properties, including inserting all logic to raise the specified property change or can execute change events. This also supports validation: if any of the fields representing a property has one or more attributes inheriting from ValidationAttribute
, those will automatically be copied over to the generated properties, so this approach is also fully supported when using ObservableValidator
to create validatable forms.
Viewmodel composition
C# doesn’t have multiple inheritance, and this can sometimes get in the way. What if there is a viewmodel that has to inherit from a specific type, but where you would also like to inject INotifyPropertyChanged
support, or have it also inherit from ObservableRecipient
to get access to its APIs? The MVVM Toolkit now includes a way to work around this, by introducing attributes for code generation that allow injecting logic from these types into arbitrary classes. These are [INotifyPropertyChanged]
, [ObservableObject]
and [ObservableRecipient]
.
Adding them to a class will cause the MVVM Toolkit source generator to include all logic from that type into that class, as if that class had also inherited from that type as well. For instance:
[INotifyPropertyChanged]
class MyObservableViewModel : DatabaseItem
{
}
This MyObservableViewModel
will inherit from DatabaseItem
as you’d expect, but the use of [INotifyPropertyChanged]
will let it also get support for INotifyPropertyChanged
, along with all the helper APIs that ObservableObject
includes on its own.
NOTE: the source generators in the MVVM Toolkit require Roslyn 4.x in order to run. As such, VS2022 or another IDE with Roslyn 4.x support is required. Using the MVVM Toolkit on VS2019 or another older IDE is still supported, but the source generators will automatically be disabled there, so features relying on them will not be supported.
🚀 .NET 6 support
This new release of the .NET Community Toolkit also adds support for .NET 6 as a new target across all available libraries. This brings several improvements when running on the latest .NET runtime:
- Trimming support is now enabled for all libraries.
- The
Count<T>()
extension in the HighPerformance package now also supportsnint
andnuint
. - Several optimizations have been introduced when on .NET 6.
Of course, all libraries will keep supporting down to .NET Standard 2.0, so you can keep referencing them from projects with different target frameworks as well. And due to how NuGet package resolution works, if you author a library using any of these packages and a lower target framework (eg. .NET Standard 2.0) and a consumer references it from a project targeting a new .NET version (eg. .NET 6), they’ll still automatically get the most optimized version of the .NET Community Toolkit assemblies that is available for them! 🙌
⚙️ Other changes
There is so much more being included in this new release!
You can see the full changelog in the GitHub release page.
Get started today! 🎉
You can find all source code in our GitHub repo, some handwritten docs on the MS Docs website, and complete API references in the .NET API browser website. If you would like to contribute, feel free to open issues or to reach out to let us know about your experience! To follow the conversation on Twitter, use the #CommunityToolkit hashtag. All your feedbacks greatly help in shape the direction of these libraries, so make sure to share them!
Happy coding! 💻
Hi Sergio, do you know when Microsoft.Toolkit.Mvvm (8.0.0) will be availlable in Nuget Package Manager in Visual Studio ?
Sergio , great to see some progress incorporating .Net 6 / Maui !!
Any idea when the Community Toolkit Windows Store Sample App will be updated to incorporate especially Maui ?
Thank you! 😄
Are you referring to the Windows Community Toolkit Sample App? In that case, that's from a separate project, the Windows Community Toolkit, not the .NET Community Toolkit, and there are no plans of it ever supporting MAUI. That Toolkit targets UWP and WinUI 3, and in the future that app might eventually move to WinUI 3, but never to MAUI. For MAUI specific stuff, I'd recommend checking out the MAUI Community Toolkit, and...
Thanks for your reply Sergio. Could you please explain the reasoning behind selecting [AlsoNotifyChangeFor] and [AlsoNotifyCanExecuteFor] as full attributes and not as parameters of the actual [Observable Property]?
If they were to make sense independently of one another, wouldn’t they be confusing?
Hi Sergio, what was the design decision behind choosing [AlsoNotifyChangeFor] and [AlsoNotifyCanExecuteFor] as full attributes instead of parameters of [ObservableProperty] itself?
They would not make sense by themselves, right?
Hi Luca, that is correct, those attributes would just be ignored if the annotated field wasn't also marked as observable (I believe there's also diagnostics that's emitted if that's the case, to help the developer). The main reason was mostly a matter of code style, especially in case multiple dependent properties or commands were available. Consider these two possible alternatives:
<code>
The second one would've been quite verbose to type, and even if splitting into separate lines...
Nit pick:
should be
as you would usually want to expose the command.
Ah, yup that wasn’t correct, thanks! Fixed 😄
CommunityToolkit.Diagnostics looks interesting. It could certainly replace guard clauses people need to make in every project.
It’s a bit of a shame though that the already existing feature for this was basically abandoned and forgotten about! https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts
Has anyone ever thought about reimplementing this for modern .NET?
url for “CommunityToolkit.Diagnostics” should be https://docs.microsoft.com/windows/communitytoolkit/diagnostics/introduction
instead of “https://docs.microsoft.comwindows/communitytoolkit/diagnostics/introduction”
(it’s missing a “/” after the domain ;))
Whoops! Fixed, thank you! 😄