Skip to main content

CommunityToolkit.Mvvm by Microsoft

Nuget / site data​

Nuget GitHub last commit GitHub Repo stars

Details​

Info​

info

Name: CommunityToolkit.Mvvm

This package includes a .NET Standard MVVM library with helpers such as:

  - ObservableObject: a base class for objects implementing the INotifyPropertyChanged interface.
- ObservableRecipient: a base class for observable objects with support for the IMessenger service.
- ObservableValidator: a base class for objects implementing the INotifyDataErrorInfo interface.
- RelayCommand: a simple delegate command implementing the ICommand interface.
- AsyncRelayCommand: a delegate command supporting asynchronous operations and cancellation.
- WeakReferenceMessenger: a messaging system to exchange messages through different loosely-coupled objects.
- StrongReferenceMessenger: a high-performance messaging system that trades weak references for speed.
- Ioc: a helper class to configure dependency injection service containers.

Author: Microsoft

NuGet: https://www.nuget.org/packages/CommunityToolkit.Mvvm

You can find more details at https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/overview

Source : https://github.com/CommunityToolkit/dotnet

Original Readme​

note

🧰 .NET Community Toolkit

.NET Community Toolkit is a collection of helpers and APIs that work for all .NET developers and are agnostic of any specific UI platform. The toolkit is maintained and published by Microsoft, and part of the .NET Foundation.

πŸ‘€ What does this repo contain?​

This repository contains several .NET libraries (originally developed as part of the Windows Community Toolkit) that can be used both by application developers (regardless on the specific UI framework in use, they work everywhere!) and library authors. These libraries are also being used internally at Microsoft to power many of our first party apps (such as the new Microsoft Store) and constantly improved by listening to feedbacks from other teams, external partners and other developers from the community. Here's a quick breakdown of the various components you'll find in this repository:

PackageLatest stableLatest PreviewDescription
CommunityToolkit.CommonCommunityToolkit.CommonCommunityToolkit.CommonA set of helper APIs shared with other CommunityToolkit libraries.
CommunityToolkit.DiagnosticsCommunityToolkit.DiagnosticsCommunityToolkit.DiagnosticsA set of helper APIs (specifically, Guard and ThrowHelper) that can be used for cleaner, more efficient and less error-prone argument validation and error checking.
CommunityToolkit.HighPerformanceCommunityToolkit.HighPerformanceCommunityToolkit.HighPerformanceA collection of helpers for working in high-performance scenarios. It includes APIs such as pooled buffer helpers, a fast string pool type, a 2D variant of Memory<T> and Span<T> (Memory2D<T> and Span2D<T>) also supporting discontiguous regions, helpers for bit shift operations (such as BitHelper, also used in Paint.NET), and more.
CommunityToolkit.Mvvm (aka MVVM Toolkit)CommunityToolkit.MvvmCommunityToolkit.MvvmA fast, modular, platform-agnostic MVVM library, which is the official successor of MvvmLight. It's used extensively in the Microsoft Store and other first party apps. The sample app repository is here.

πŸ™Œ Getting Started​

Please read the Getting Started with the .NET Community Toolkit page for more detailed information.

πŸ“ƒ Documentation​

All documentation for the toolkit is hosted on Microsoft Docs.

All API documentation can be found at the .NET API Browser.

πŸš€ Contribution​

Do you want to contribute?

Check out our .NET Community Toolkit Wiki page to learn more about contribution and guidelines!

πŸ“¦ NuGet Packages​

NuGet is a standard package manager for .NET applications which is built into Visual Studio. When you open solution in Visual Studio, choose the Tools menu > NuGet Package Manager > Manage NuGet packages for solution… Enter one of the package names mentioned in .NET Community Toolkit NuGet Packages table to search for it online.

🌍 Roadmap​

Read what we plan for next iterations, and feel free to ask questions.

Check out our Preview Packages Wiki Page to learn more about updating your NuGet sources in Visual Studio, then you can also get pre-release packages of upcoming versions to try.

πŸ“„ Code of Conduct​

This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. For more information see the .NET Foundation Code of Conduct.

🏒 .NET Foundation​

This project is supported by the .NET Foundation.

πŸ† Contributors​

Toolkit Contributors

Made with contrib.rocks.

About​

note

Shows how to implement INotifyPropertyChanged,ObservableProperty and RelayCommand

Unfortunately , not yet a separate package just for those.

Also, this show that RSCG could generate multiple partial declarations

How to use​

Example ( source csproj, source files )​

This is the CSharp Project that references CommunityToolkit.Mvvm

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>

Generated Files​

Those are taken from $(BaseIntermediateOutputPath)\GX

// <auto-generated/>
#pragma warning disable
#nullable enable
namespace test
{
/// <inheritdoc/>
partial class MyViewModel : global::System.ComponentModel.INotifyPropertyChanged
{
/// <inheritdoc cref = "global::System.ComponentModel.INotifyPropertyChanged.PropertyChanged"/>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public event global::System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Raises the <see cref = "PropertyChanged"/> event.
/// </summary>
/// <param name = "e">The input <see cref = "global::System.ComponentModel.PropertyChangedEventArgs"/> instance.</param>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}

/// <summary>
/// Raises the <see cref = "PropertyChanged"/> event.
/// </summary>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected void OnPropertyChanged([global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
OnPropertyChanged(new global::System.ComponentModel.PropertyChangedEventArgs(propertyName));
}

/// <summary>
/// Compares the current and new values for a given property. If the value has changed, updates
/// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
/// </summary>
/// <typeparam name = "T">The type of the property that changed.</typeparam>
/// <param name = "field">The field storing the property's value.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
/// <remarks>
/// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetProperty<T>([global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("newValue")] ref T field, T newValue, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
if (global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(field, newValue))
{
return false;
}

field = newValue;
OnPropertyChanged(propertyName);
return true;
}

/// <summary>
/// Compares the current and new values for a given property. If the value has changed, updates
/// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
/// See additional notes about this overload in <see cref = "SetProperty{T}(ref T, T, string)"/>.
/// </summary>
/// <typeparam name = "T">The type of the property that changed.</typeparam>
/// <param name = "field">The field storing the property's value.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "comparer">The <see cref = "global::System.Collections.Generic.IEqualityComparer{T}"/> instance to use to compare the input values.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetProperty<T>([global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("newValue")] ref T field, T newValue, global::System.Collections.Generic.IEqualityComparer<T> comparer, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
if (comparer.Equals(field, newValue))
{
return false;
}

field = newValue;
OnPropertyChanged(propertyName);
return true;
}

/// <summary>
/// Compares the current and new values for a given property. If the value has changed, updates
/// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
/// This overload is much less efficient than <see cref = "SetProperty{T}(ref T, T, string)"/> and it
/// should only be used when the former is not viable (eg. when the target property being
/// updated does not directly expose a backing field that can be passed by reference).
/// For performance reasons, it is recommended to use a stateful callback if possible through
/// the <see cref = "SetProperty{TModel, T}(T, T, TModel, global::System.Action{TModel, T}, string? )"/> whenever possible
/// instead of this overload, as that will allow the C# compiler to cache the input callback and
/// reduce the memory allocations. More info on that overload are available in the related XML
/// docs. This overload is here for completeness and in cases where that is not applicable.
/// </summary>
/// <typeparam name = "T">The type of the property that changed.</typeparam>
/// <param name = "oldValue">The current property value.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "callback">A callback to invoke to update the property value.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
/// <remarks>
/// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetProperty<T>(T oldValue, T newValue, global::System.Action<T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
if (global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}

callback(newValue);
OnPropertyChanged(propertyName);
return true;
}

/// <summary>
/// Compares the current and new values for a given property. If the value has changed, updates
/// the property with the new value, then raises the <see cref = "PropertyChanged"/> event.
/// See additional notes about this overload in <see cref = "SetProperty{T}(T, T, global::System.Action{T}, string)"/>.
/// </summary>
/// <typeparam name = "T">The type of the property that changed.</typeparam>
/// <param name = "oldValue">The current property value.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "comparer">The <see cref = "global::System.Collections.Generic.IEqualityComparer{T}"/> instance to use to compare the input values.</param>
/// <param name = "callback">A callback to invoke to update the property value.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetProperty<T>(T oldValue, T newValue, global::System.Collections.Generic.IEqualityComparer<T> comparer, global::System.Action<T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
if (comparer.Equals(oldValue, newValue))
{
return false;
}

callback(newValue);
OnPropertyChanged(propertyName);
return true;
}

/// <summary>
/// Compares the current and new values for a given nested property. If the value has changed,
/// updates the property and then raises the <see cref = "PropertyChanged"/> event.
/// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>,
/// with the difference being that this method is used to relay properties from a wrapped model in the
/// current instance. This type is useful when creating wrapping, bindable objects that operate over
/// models that lack support for notification (eg. for CRUD operations).
/// Suppose we have this model (eg. for a database row in a table):
/// <code>
/// public class Person
/// {
/// public string Name { get; set; }
/// }
/// </code>
/// We can then use a property to wrap instances of this type into our observable model (which supports
/// notifications), injecting the notification to the properties of that model, like so:
/// <code>
/// [INotifyPropertyChanged]
/// public partial class BindablePerson
/// {
/// public Model { get; }
///
/// public BindablePerson(Person model)
/// {
/// Model = model;
/// }
///
/// public string Name
/// {
/// get => Model.Name;
/// set => Set(Model.Name, value, Model, (model, name) => model.Name = name);
/// }
/// }
/// </code>
/// This way we can then use the wrapping object in our application, and all those "proxy" properties will
/// also raise notifications when changed. Note that this method is not meant to be a replacement for
/// <see cref = "SetProperty{T}(ref T, T, string)"/>, and it should only be used when relaying properties to a model that
/// doesn't support notifications, and only if you can't implement notifications to that model directly (eg. by having
/// it implement <see cref = "global::System.ComponentModel.INotifyPropertyChanged"/>). The syntax relies on passing the target model and a stateless callback
/// to allow the C# compiler to cache the function, which results in much better performance and no memory usage.
/// </summary>
/// <typeparam name = "TModel">The type of model whose property (or field) to set.</typeparam>
/// <typeparam name = "T">The type of property (or field) to set.</typeparam>
/// <param name = "oldValue">The current property value.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "model">The model containing the property being updated.</param>
/// <param name = "callback">The callback to invoke to set the target property value, if a change has occurred.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
/// <remarks>
/// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetProperty<TModel, T>(T oldValue, T newValue, TModel model, global::System.Action<TModel, T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
where TModel : class
{
if (global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}

callback(model, newValue);
OnPropertyChanged(propertyName);
return true;
}

/// <summary>
/// Compares the current and new values for a given nested property. If the value has changed,
/// updates the property and then raises the <see cref = "PropertyChanged"/> event.
/// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>,
/// with the difference being that this method is used to relay properties from a wrapped model in the
/// current instance. See additional notes about this overload in <see cref = "SetProperty{TModel, T}(T, T, TModel, global::System.Action{TModel, T}, string)"/>.
/// </summary>
/// <typeparam name = "TModel">The type of model whose property (or field) to set.</typeparam>
/// <typeparam name = "T">The type of property (or field) to set.</typeparam>
/// <param name = "oldValue">The current property value.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "comparer">The <see cref = "global::System.Collections.Generic.IEqualityComparer{T}"/> instance to use to compare the input values.</param>
/// <param name = "model">The model containing the property being updated.</param>
/// <param name = "callback">The callback to invoke to set the target property value, if a change has occurred.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetProperty<TModel, T>(T oldValue, T newValue, global::System.Collections.Generic.IEqualityComparer<T> comparer, TModel model, global::System.Action<TModel, T> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
where TModel : class
{
if (comparer.Equals(oldValue, newValue))
{
return false;
}

callback(model, newValue);
OnPropertyChanged(propertyName);
return true;
}

/// <summary>
/// Compares the current and new values for a given field (which should be the backing field for a property).
/// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
/// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>, with the difference being that
/// this method will also monitor the new value of the property (a generic <see cref = "global::System.Threading.Tasks.Task"/>) and will also
/// raise the <see cref = "PropertyChanged"/> again for the target property when it completes.
/// This can be used to update bindings observing that <see cref = "global::System.Threading.Tasks.Task"/> or any of its properties.
/// This method and its overload specifically rely on the <see cref = "TaskNotifier"/> type, which needs
/// to be used in the backing field for the target <see cref = "global::System.Threading.Tasks.Task"/> property. The field doesn't need to be
/// initialized, as this method will take care of doing that automatically. The <see cref = "TaskNotifier"/>
/// type also includes an implicit operator, so it can be assigned to any <see cref = "global::System.Threading.Tasks.Task"/> instance directly.
/// Here is a sample property declaration using this method:
/// <code>
/// private TaskNotifier myTask;
///
/// public Task MyTask
/// {
/// get => myTask;
/// private set => SetAndNotifyOnCompletion(ref myTask, value);
/// }
/// </code>
/// </summary>
/// <param name = "taskNotifier">The field notifier to modify.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
/// <remarks>
/// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are
/// the same. The return value being <see langword="true"/> only indicates that the new value being assigned to
/// <paramref name = "taskNotifier"/> is different than the previous one, and it does not mean the new
/// <see cref = "global::System.Threading.Tasks.Task"/> instance passed as argument is in any particular state.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetPropertyAndNotifyOnCompletion([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier? taskNotifier, global::System.Threading.Tasks.Task? newValue, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier(), newValue, null, propertyName);
}

/// <summary>
/// Compares the current and new values for a given field (which should be the backing field for a property).
/// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
/// This method is just like <see cref = "SetPropertyAndNotifyOnCompletion(ref TaskNotifier, global::System.Threading.Tasks.Task, string)"/>,
/// with the difference being an extra <see cref = "global::System.Action{T}"/> parameter with a callback being invoked
/// either immediately, if the new task has already completed or is <see langword="null"/>, or upon completion.
/// </summary>
/// <param name = "taskNotifier">The field notifier to modify.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "callback">A callback to invoke to update the property value.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
/// <remarks>
/// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetPropertyAndNotifyOnCompletion([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier? taskNotifier, global::System.Threading.Tasks.Task? newValue, global::System.Action<global::System.Threading.Tasks.Task?> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier(), newValue, callback, propertyName);
}

/// <summary>
/// Compares the current and new values for a given field (which should be the backing field for a property).
/// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
/// The behavior mirrors that of <see cref = "SetProperty{T}(ref T, T, string)"/>, with the difference being that
/// this method will also monitor the new value of the property (a generic <see cref = "global::System.Threading.Tasks.Task"/>) and will also
/// raise the <see cref = "PropertyChanged"/> again for the target property when it completes.
/// This can be used to update bindings observing that <see cref = "global::System.Threading.Tasks.Task"/> or any of its properties.
/// This method and its overload specifically rely on the <see cref = "TaskNotifier{T}"/> type, which needs
/// to be used in the backing field for the target <see cref = "global::System.Threading.Tasks.Task"/> property. The field doesn't need to be
/// initialized, as this method will take care of doing that automatically. The <see cref = "TaskNotifier{T}"/>
/// type also includes an implicit operator, so it can be assigned to any <see cref = "global::System.Threading.Tasks.Task"/> instance directly.
/// Here is a sample property declaration using this method:
/// <code>
/// private TaskNotifier&lt;int&gt; myTask;
///
/// public Task&lt;int&gt; MyTask
/// {
/// get => myTask;
/// private set => SetAndNotifyOnCompletion(ref myTask, value);
/// }
/// </code>
/// </summary>
/// <typeparam name = "T">The type of result for the <see cref = "global::System.Threading.Tasks.Task{TResult}"/> to set and monitor.</typeparam>
/// <param name = "taskNotifier">The field notifier to modify.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
/// <remarks>
/// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are
/// the same. The return value being <see langword="true"/> only indicates that the new value being assigned to
/// <paramref name = "taskNotifier"/> is different than the previous one, and it does not mean the new
/// <see cref = "global::System.Threading.Tasks.Task{TResult}"/> instance passed as argument is in any particular state.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetPropertyAndNotifyOnCompletion<T>([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier<T>? taskNotifier, global::System.Threading.Tasks.Task<T>? newValue, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier<T>(), newValue, null, propertyName);
}

/// <summary>
/// Compares the current and new values for a given field (which should be the backing field for a property).
/// If the value has changed, updates the field and then raises the <see cref = "PropertyChanged"/> event.
/// This method is just like <see cref = "SetPropertyAndNotifyOnCompletion{T}(ref TaskNotifier{T}, global::System.Threading.Tasks.Task{T}, string)"/>,
/// with the difference being an extra <see cref = "global::System.Action{T}"/> parameter with a callback being invoked
/// either immediately, if the new task has already completed or is <see langword="null"/>, or upon completion.
/// </summary>
/// <typeparam name = "T">The type of result for the <see cref = "global::System.Threading.Tasks.Task{TResult}"/> to set and monitor.</typeparam>
/// <param name = "taskNotifier">The field notifier to modify.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "callback">A callback to invoke to update the property value.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
/// <remarks>
/// The <see cref = "PropertyChanged"/> event is not raised if the current and new value for the target property are the same.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected bool SetPropertyAndNotifyOnCompletion<T>([global::System.Diagnostics.CodeAnalysis.NotNull] ref TaskNotifier<T>? taskNotifier, global::System.Threading.Tasks.Task<T>? newValue, global::System.Action<global::System.Threading.Tasks.Task<T>?> callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
{
return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new TaskNotifier<T>(), newValue, callback, propertyName);
}

/// <summary>
/// Implements the notification logic for the related methods.
/// </summary>
/// <typeparam name = "TTask">The type of <see cref = "global::System.Threading.Tasks.Task"/> to set and monitor.</typeparam>
/// <param name = "taskNotifier">The field notifier.</param>
/// <param name = "newValue">The property's value after the change occurred.</param>
/// <param name = "callback">(optional) A callback to invoke to update the property value.</param>
/// <param name = "propertyName">(optional) The name of the property that changed.</param>
/// <returns><see langword="true"/> if the property was changed, <see langword="false"/> otherwise.</returns>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
private bool SetPropertyAndNotifyOnCompletion<TTask>(ITaskNotifier<TTask> taskNotifier, TTask? newValue, global::System.Action<TTask?>? callback, [global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
where TTask : global::System.Threading.Tasks.Task
{
if (ReferenceEquals(taskNotifier.Task, newValue))
{
return false;
}

bool isAlreadyCompletedOrNull = newValue?.IsCompleted ?? true;
taskNotifier.Task = newValue;
OnPropertyChanged(propertyName);
if (isAlreadyCompletedOrNull)
{
if (callback != null)
{
callback(newValue);
}

return true;
}

async void MonitorTask()
{
await global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__TaskExtensions.GetAwaitableWithoutEndValidation(newValue!);
if (ReferenceEquals(taskNotifier.Task, newValue))
{
OnPropertyChanged(propertyName);
}

if (callback != null)
{
callback(newValue);
}
}

MonitorTask();
return true;
}

/// <summary>
/// An interface for task notifiers of a specified type.
/// </summary>
/// <typeparam name = "TTask">The type of value to store.</typeparam>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
private interface ITaskNotifier<TTask>
where TTask : global::System.Threading.Tasks.Task
{
/// <summary>
/// Gets or sets the wrapped <typeparamref name = "TTask"/> value.
/// </summary>
TTask? Task { get; set; }
}

/// <summary>
/// A wrapping class that can hold a <see cref = "global::System.Threading.Tasks.Task"/> value.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected sealed class TaskNotifier : ITaskNotifier<global::System.Threading.Tasks.Task>
{
/// <summary>
/// Initializes a new instance of the <see cref = "TaskNotifier"/> class.
/// </summary>
internal TaskNotifier()
{
}

private global::System.Threading.Tasks.Task? task;
/// <inheritdoc/>
global::System.Threading.Tasks.Task? ITaskNotifier<global::System.Threading.Tasks.Task>.Task { get => this.task; set => this.task = value; }

/// <summary>
/// Unwraps the <see cref = "global::System.Threading.Tasks.Task"/> value stored in the current instance.
/// </summary>
/// <param name = "notifier">The input <see cref = "TaskNotifier{TTask}"/> instance.</param>
public static implicit operator global::System.Threading.Tasks.Task? (TaskNotifier? notifier)
{
return notifier?.task;
}
}

/// <summary>
/// A wrapping class that can hold a <see cref = "global::System.Threading.Tasks.Task{T}"/> value.
/// </summary>
/// <typeparam name = "T">The type of value for the wrapped <see cref = "global::System.Threading.Tasks.Task{T}"/> instance.</typeparam>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.2.0.0")]
[global::System.Diagnostics.DebuggerNonUserCode]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
protected sealed class TaskNotifier<T> : ITaskNotifier<global::System.Threading.Tasks.Task<T>>
{
/// <summary>
/// Initializes a new instance of the <see cref = "TaskNotifier{TTask}"/> class.
/// </summary>
internal TaskNotifier()
{
}

private global::System.Threading.Tasks.Task<T>? task;
/// <inheritdoc/>
global::System.Threading.Tasks.Task<T>? ITaskNotifier<global::System.Threading.Tasks.Task<T>>.Task { get => this.task; set => this.task = value; }

/// <summary>
/// Unwraps the <see cref = "global::System.Threading.Tasks.Task{T}"/> value stored in the current instance.
/// </summary>
/// <param name = "notifier">The input <see cref = "TaskNotifier{TTask}"/> instance.</param>
public static implicit operator global::System.Threading.Tasks.Task<T>? (TaskNotifier<T>? notifier)
{
return notifier?.task;
}
}
}
}

Usefull​

Download Example (.NET C# )​

Share CommunityToolkit.Mvvm​

https://ignatandrei.github.io/RSCG_Examples/v2/docs/CommunityToolkit.Mvvm

In the same category (MVVM) - 1 other generators​

PropertyChangedSourceGenerator​