Announcing .NET Community Toolkit v8.0.0 Preview 1

Sergio Pedri

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:

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 supports nint and nuint.
  • 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! 💻

10 comments

Comments are closed. Login to edit/delete your existing comments

  • Nicolas Musset

    Nit pick:

    private IRelayCommand GreetUserCommand => greetUserCommand ??= new RelayCommand(GreetUser);

    should be

    public IRelayCommand GreetUserCommand => greetUserCommand ??= new RelayCommand(GreetUser);

    as you would usually want to expose the command.

  • Luca Spolidoro

    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?

    • Sergio PedriMicrosoft employee

      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:

      [ObservableProperty]
      [AlsoNotifyChangeFor(nameof(FullName), nameof(CanGreetUser))]
      [AlsoNotifyCanExecuteFor(nameof(GreetUserCommand))]
      private string? firstName;
      
      [ObservableProperty(AlsoNotifyChangeFor = new[] { nameof(FullName), nameof(CanGreetUser) }, AlsoNotifyCanExecuteFor = nameof(GreetUserCommand))]
      private string? firstName;

      The second one would’ve been quite verbose to type, and even if splitting into separate lines it would’ve still been not ideal. Using separate attributes instead makes the whole declaration easier to read and to type. Note that all attributes for source generators are completely stripped away when compiling, so they won’t impact the final IL size at all, they’re purely used to instruct the source generators.

      Hope this helps! 🙂

  • Johan Mulder

    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 ?

    • Sergio PedriMicrosoft employee

      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 their samples. On a related note though, we definitely want to add support for MAUI to the MVVM Toolkit sample app in the future 🙂

  • Fabien POTEL

    Hi Sergio, do you know when Microsoft.Toolkit.Mvvm (8.0.0) will be availlable in Nuget Package Manager in Visual Studio ?