December 12th, 2024

Announcing .NET Community Toolkit 8.4! Partial properties support for MVVM, new analyzers, and more!

Sergio Pedri
Senior Software Engineer

We’re happy to announce the official launch of the 8.4 release of the .NET Community Toolkit! This new version includes support for partial properties for the MVVM Toolkit generators, new analyzers, bug fixes and enhancements, and more!

.NET Community Toolkit 8.4.0

As always, we deeply appreciate all the feedback received both by teams here at Microsoft using the Toolkit, as well as other developers in the community. All the issues, bug reports, comments and feedback continue to be extremely useful for us to plan and prioritize feature work across the entire .NET Community Toolkit. Thank you to everyone contributing to this project and helping make the .NET Community Toolkit better! 🎉

Information

For more details on the history of the .NET Community Toolkit, here is a link to our previous 8.0.0 announcement post.

Here is a breakdown of the main changes that are included in this new 8.4 release of the .NET Community Toolkit.

Partial properties support for the MVVM Toolkit 🎉

One of the most popular feature requests for the MVVM Toolkit source generators was support for partial properties, which is now available thanks to the new C# language features available in the .NET 9 SDK! Specifically, the MVVM Toolkit source generators will now leverage partial properties and semi-auto properties (aka. the field keyword) to make it possible to define observable properties as follows:

Using the [ObservableProperty] attribute on a partial property, in a partial class that derives from ObservableObject

This comes with significant improvements:

  • Declaring properties is now properly integrated with the C# language, instead of the MVVM Toolkit creating a new property inferring its characteristics based on the annotated field. This means all C# features you’d expect to work on properties will now “just work”, such as declaring custom accessibility modifiers to each accessor, or annotating the property, field, or accessors, with custom attributes.
  • Similarly, more modifers are now supported as well, such as new, sealed, override, and required.
  • Nullability annotations are improved, and also correctly handle property initializers and constructors.
  • Using partial properties also makes [ObservableProperty] fully AOT safe for UWP and WinUI 3!
  • In an upcoming Visual Studio update, CTRL + click (and F12) navigation will allow easily jumping between the partial property declaration and the implementation, meaning that jumping back to property declarations from arbitrary property references elsewhere in the code will be much easier than before.

Of course, this new release also includes a brand new code fixer, which can automate migrating code form [ObservableProperty] on fields, to partial properties, with a single click to fix all occurrences in your entire solution! Just hover on the squiggly line in Visual Studio and select the suggested code fix:

Menu flying UI in Visual Studio showing the code fixer for the MVVMTK0042 diagnostic, suggesting to change a field using [ObservableProperty] into a partial property

We recommend converting all [ObservableProperty] uses to partial properties, especially if you’re using CsWinRT (ie. if you have a UWP .NET 9 app or a WinUI 3 app) and need Native AOT support. You will get clearer code, better language support and lots of new features. Try it out and please share feedback!

Note

In order to use support for partial properties, C# preview is required. You can enable this by adding <LangVersion>preview</LangVersion> to your .csproj file, or to any imported .props file (eg. Directory.Build.props). This is necessary because the generated code makes use of the field keyword.

Lots of new MVVM Toolkit analyzers 🎯

One of the main areas of investment in the MVVM Toolkit is in our rich set of diagnostic analyzers, which help make sure code is written correctly, and without common mistakes. These analyzers cover all sort of things (eg. unsupported types for [ObservableProperty] members, incorrect declarations for annotated types, etc.), and new analyzers are added in each release of the MVVM Toolkit. The 8.4 release follows this trend, and introduces the following new diagnostics:

  • MVVMTK0041, error: “The C# language version must be set to ‘preview’ when using [ObservableProperty] on partial properties for the source generators to emit valid code (the <LangVersion>preview</LangVersion> option must be set in the .csproj/.props file).”.
  • MVVMTK0042, error: “Fields using [ObservableProperty] can be converted to partial properties instead, which is recommended (doing so improves the developer experience and allows other generators and analyzers to correctly see the generated property as well).”.
  • MVVMTK0043, error: “Properties annotated with [ObservableProperty] must be instance (non static) partial properties with a getter and a setter that is not init-only.”.
  • MVVMTK0044, error: “Using [ObservableProperty] with (partial) properties requires a higher version of Roslyn (remove [ObservableProperty] or target a field instead, or upgrade to at least Visual Studio 2022 version 17.12 and the .NET 9 SDK).”.
  • MVVMTK0045, warning: “Fields using [ObservableProperty] will generate code that is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and partial properties should be used instead (as they allow the CsWinRT generators to correctly produce the necessary WinRT marshalling code).”.
  • MVVMTK0046, warning: “Using [RelayCommand] on methods within a type also using [GeneratedBindableCustomProperty] is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated command property that is produced by the MVVM Toolkit generator).”.
  • MVVMTK0047, warning: “Using [GeneratedBindableCustomProperty] on types that also use [ObservableProperty] on any declared (or inherited) fields is not supported, and partial properties should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator).”.
  • MVVMTK0048, warning: “Using [GeneratedBindableCustomProperty] on types that also use [RelayCommand] on any inherited methods is not supported, and a manually declared command property should be used instead (the [GeneratedBindableCustomProperty] generator cannot see the generated property that is produced by the MVVM Toolkit generator).”.
  • MVVMTK0049, warning: “Using the [INotifyPropertyChanged] attribute on types is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and they should derive from ObservableObject or manually implement INotifyPropertyChanged instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code).”.
  • MVVMTK0050, warning: “Using the [ObservableObject] attribute on types is not AOT compatible in WinRT scenarios (such as UWP XAML and WinUI 3 apps), and they should derive from ObservableObject instead (as it allows the CsWinRT generators to correctly produce the necessary WinRT marshalling code).”.
  • MVVMTK0051, info: “This project producing one or more ‘MVVMTK0045’ warnings due to [ObservableProperty] being used on fields, which is not AOT compatible in WinRT scenarios, should set ‘LangVersion’ to ‘preview’ to enable partial properties and the associated code fixer (setting ‘LangVersion=preview’ is required to use [ObservableProperty] on partial properties and address these warnings).”.
  • MVVMTK0052, error: “A property using [ObservableProperty] is not an incomplete partial definition part ([ObservableProperty] must be used on partial property definitions with no implementation part).”.
  • MVVMTK0053, error: “A property using [ObservableProperty] returns a value by reference ([ObservableProperty] must be used on properties returning a type by value).”.
  • MVVMTK0054, error: “A property using [ObservableProperty] returns a byref-like value ([ObservableProperty] must be used on properties of a non byref-like type).”.
  • MVVMTK0055, error: “A property using [ObservableProperty] returns a pointer-like value ([ObservableProperty] must be used on properties of a non pointer-like type).”.
  • MVVMTK0056, info: “Semi-auto properties should be converted to partial properties using [ObservableProperty] when possible, which is recommended (doing so makes the code less verbose and results in more optimized code).”.

As you can see, we have so many new analyzers in this release! They fall into two categories:

  • General code analysis for MVVM scenarios
  • CsWinRT trim/AOT supporting code analysis (for UWP and WinUI 3)

All of these will just show up nicely in quick info, whenever needed:

Example of a new diagnostic (MVVMTK0042, suggesting to convert fields using [ObservableProperty] to partial properties) showing up in the quick info flyout in Visual Studio

We want you to be able to rely on the MVVM Toolkit to make sure code leveraging its generators is written correctly and bug-free! Please let us know if you hit any issues with the analyzers, or if you found a new scenario that is not covered, and that you think should have a new dedicated analyzer, by opening an issue in our GitHub repo.

Other changes and improvements ✅

  • Add .targets to validate the Windows SDK version (#942): the MVVM Toolkit now includes MSBuild logic to provide friendly error messages when using an incorrect version of the Windows SDK package, and suggests how to fix it and which exact version to use.
  • Allow forwarding attributes to property accessors (#952): when using [ObservableProperty] on fields, adding attributes on the generated accessors is now also supported.
  • Fix suppressions for custom attribute targets (#964): the custom diagnostic suppressions now work correctly when using custom attribute targets on [ObservableProperty] fields.
  • Move some diagnostics to analyzers (#968): moved more diagnostics to separate analyzers, to improve performance of the MVVM Toolkit source generators.
  • Handle ‘required’ fields in partial property code fixer (#972): using the required modifier is now also support for fields using [ObservableProperty].
  • Embed .pdb files for all analyzer projects (#980): all source generators and analyzers now have embedded .pdb files, making it simpler to debug them in Visual Studio, if needed.
  • Use ref readonly in IndexOf<T> (#997): the IndexOf<T> extension now takes a ref readonly, making it clear that it’s not meant to be used with rvalue-s.
  • Added Stream over ReadOnlySequence<byte> (#808): there’s a new AsStream() extension for ReadOnlySequence<byte> to easily get a readonly, seekable stream wrapping it! Thank you @paulomorgado!

You can see the full changelog for this release from the GitHub release page.

Get started today! 🎉

You can find all source code in our GitHub repo, some handwritten docs on MS learn, 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 feedback greatly helped shape the direction of these libraries, so make sure to share them!

Happy coding! 💻

Author

Sergio Pedri
Senior Software Engineer

I'm a Senior Software Engineer at Microsoft, working on the new Microsoft Store app for Windows 11 and Windows 10, and the maintainer of the .NET Community Toolkit, a suite of open source libraries to help all .NET developers. I started writing Windows and .NET apps in 2013 with Silverlight and later WinRT, and then moved to UWP. I'm passionate about open source software and a member of the .NET Foundation since 2020, and I regularly contribute to several projects on GitHub (such as ...

More about author

1 comment

  • Yu Shiuan Zhuang 10 hours ago

    This is amazing! thanks for all members of community to help this toolkit better!

'; block.insertAdjacentElement('beforebegin', codeheader); let button = codeheader.querySelector('.copy-button'); button.addEventListener("click", async () => { let blockToCopy = block; await copyCode(blockToCopy, button); }); } }); async function copyCode(blockToCopy, button) { let code = blockToCopy.querySelector("code"); let text = ''; if (code) { text = code.innerText; } else { text = blockToCopy.innerText; } try { await navigator.clipboard.writeText(text); } catch (err) { console.error('Failed to copy:', err); } button.innerText = "Copied"; setTimeout(() => { button.innerHTML = '' + svgCodeIcon + ' Copy'; }, 1400); }