diff --git a/docs/mvvm/AsyncRelayCommand.md b/docs/mvvm/AsyncRelayCommand.md index 6c0b03a..cdf7afc 100644 --- a/docs/mvvm/AsyncRelayCommand.md +++ b/docs/mvvm/AsyncRelayCommand.md @@ -9,7 +9,9 @@ dev_langs: # AsyncRelayCommand and AsyncRelayCommand<T> -The [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.AsyncRelayCommand) and [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.AsyncRelayCommand-1) are `ICommand` implementations that extend the functionalities offered by `RelayCommand`, with support for asynchronous operations. +The [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.AsyncRelayCommand) and [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.AsyncRelayCommand-1) are `ICommand` implementations that extend the functionalities offered by [`RelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.RelayCommand), with support for asynchronous operations. + +> **Platform APIs:** [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.AsyncRelayCommand), [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.AsyncRelayCommand-1), [`RelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.RelayCommand), [`IAsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IAsyncRelayCommand), [`IAsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IAsyncRelayCommand-1) ## How they work @@ -18,7 +20,7 @@ The [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolki - They extend the functionalities of the synchronous commands included in the library, with support for `Task`-returning delegates. - They can wrap asynchronous functions with an additional `CancellationToken` parameter to support cancelation, and they expose a `CanBeCanceled` and `IsCancellationRequested` properties, as well as a `Cancel` method. - 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` 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. +- They implement the [`IAsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IAsyncRelayCommand) and [`IAsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IAsyncRelayCommand-1) 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. ## Working with asynchronous commands @@ -75,18 +77,6 @@ With the related UI code: 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). -## Sample Code +## Examples -There are more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). - -## Requirements - -| Device family | Universal, 10.0.16299.0 or higher | -| --- | --- | -| Namespace | Microsoft.Toolkit.Mvvm | -| NuGet package | [Microsoft.Toolkit.Mvvm](https://www.nuget.org/packages/Microsoft.Toolkit.Mvvm/) | - -## API - -* [AsyncRelayCommand source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand.cs) -* [AsyncRelayCommand<T> source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand{T}.cs) +You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). \ No newline at end of file diff --git a/docs/mvvm/Ioc.md b/docs/mvvm/Ioc.md index e435c67..640aad7 100644 --- a/docs/mvvm/Ioc.md +++ b/docs/mvvm/Ioc.md @@ -13,6 +13,8 @@ A common pattern that can be used to increase modularity in the codebase of an a 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. +> **Platform APIs:** [`Ioc`](Microsoft.Toolkit.Mvvm.DependencyInjection.Ioc) + ## Configure and resolve services The first step is to declare an `IServiceProvider` instance, and to initialize all the necessary services, usually at startup. For instance, on UWP (but a similar setup can be used on other frameworks too): @@ -147,17 +149,6 @@ public ContactsView() For more info about `Microsoft.Extensions.DependencyInjection`, see [here](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection). -## Sample Code +## Examples -There are more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). - -## Requirements - -| Device family | Universal, 10.0.16299.0 or higher | -| --- | --- | -| Namespace | Microsoft.Toolkit.Mvvm | -| NuGet package | [Microsoft.Toolkit.Mvvm](https://www.nuget.org/packages/Microsoft.Toolkit.Mvvm/) | - -## API - -* [ObservableObject source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableObject.cs) +You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). \ No newline at end of file diff --git a/docs/mvvm/Messenger.md b/docs/mvvm/Messenger.md index 5bb37ee..9f5bf70 100644 --- a/docs/mvvm/Messenger.md +++ b/docs/mvvm/Messenger.md @@ -11,11 +11,13 @@ dev_langs: The [`IMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.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`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.WeakReferenceMessenger) and [`StrongReferenceMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.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. +> **Platform APIs:** [`IMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.IMessenger), [`WeakReferenceMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.WeakReferenceMessenger), [`StrongReferenceMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.StrongReferenceMessenger), [`IRecipient`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.irecipient-1), [`MessageHandler`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.messagehandler-2), [`ObservableRecipient`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.ComponentModel.ObservableRecipient), [`RequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.RequestMessage-1), [`AsyncRequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.AsyncRequestMessage-1), [`CollectionRequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.CollectionRequestMessage-1), [`AsyncCollectionRequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.AsyncCollectionRequestMessage-1). + ## 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` interface, or using a `MessageHandler` 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. +There are two ways to perform message registration: either through the [`IRecipient`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.irecipient-1) interface, or using a [`MessageHandler`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.messagehandler-2) 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. Both `WeakReferenceMessenger` and `StrongReferenceMessenger` also expose a `Default` property that offers a thread-safe implementation built-in into the package. It is also possible to create multiple messenger instances if needed, for instance if a different one is injected with a DI service provider into a different module of the app (for instance, multiple windows running in the same process). @@ -91,7 +93,7 @@ WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user)); ## Using request messages -Another useful feature of messenger instances is that they can also be used to request values from a module to another. In order to do so, the package includes a base `RequestMessage` class, which can be used like so: +Another useful feature of messenger instances is that they can also be used to request values from a module to another. In order to do so, the package includes a base [`RequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.RequestMessage-1) class, which can be used like so: ```csharp // Create a message @@ -114,7 +116,7 @@ User user = WeakReferenceMessenger.Default.Send(); The `RequestMessage` 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`, `CollectionRequestMessage` and `AsyncCollectionRequestMessage`. +The same namespace also includes base requests message for other scenarios: [`AsyncRequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.AsyncRequestMessage-1), [`CollectionRequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.CollectionRequestMessage-1) and [`AsyncCollectionRequestMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.AsyncCollectionRequestMessage-1). Here's how you can use an async request message: ```csharp @@ -133,19 +135,6 @@ WeakReferenceMessenger.Default.Register User user = await WeakReferenceMessenger.Default.Send(); ``` -## Sample Code +## Examples -There are more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). - -## Requirements - -| Device family | Universal, 10.0.16299.0 or higher | -| --- | --- | -| Namespace | Microsoft.Toolkit.Mvvm | -| NuGet package | [Microsoft.Toolkit.Mvvm](https://www.nuget.org/packages/Microsoft.Toolkit.Mvvm/) | - -## API - -* [IMessenger source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/Messaging/IMessenger.cs) -* [StrongReferenceMessenger source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/Messaging/StrongReferenceMessenger.cs) -* [WeakReferenceMessenger source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/Messaging/WeakReferenceMessenger.cs) +You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). \ No newline at end of file diff --git a/docs/mvvm/ObservableObject.md b/docs/mvvm/ObservableObject.md index 9bfd8b7..3d2959e 100644 --- a/docs/mvvm/ObservableObject.md +++ b/docs/mvvm/ObservableObject.md @@ -11,6 +11,8 @@ dev_langs: The [`ObservableObject`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject) is a base class for objects that are observable by implementing the [`INotifyPropertyChanged`](https://docs.microsoft.com/dotnet/api/system.componentmodel.inotifypropertychanged) and [`INotifyPropertyChanging`](https://docs.microsoft.com/dotnet/api/system.componentmodel.inotifypropertychanging) interfaces. It can be used as a starting point for all kinds of objects that need to support property change notifications. +> **Platform APIs:** [`ObservableObject`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject), [`TaskNotifier`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject.TaskNotifier), [`TaskNotifier`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject.TaskNotifier-1) + ## How it works `ObservableObject` has the following main features: @@ -94,22 +96,11 @@ public class MyModel : ObservableObject } ``` -Here the `SetPropertyAndNotifyOnCompletion(ref TaskNotifier, Task, string)` method will take care of updating the target field, monitoring the new task, if present, and raising the notification event when that task completes. This way, it's possible to just bind to a task property and to be notified when its status changes. The `TaskNotifier` is a special type exposed by `ObservableObject` that wraps a target `Task` instance and enables the necessary notification logic for this method. The `TaskNotifier` type is also available to use directly if you have a general `Task` only. +Here the `SetPropertyAndNotifyOnCompletion(ref TaskNotifier, Task, string)` method will take care of updating the target field, monitoring the new task, if present, and raising the notification event when that task completes. This way, it's possible to just bind to a task property and to be notified when its status changes. The [`TaskNotifier`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject.TaskNotifier-1) is a special type exposed by `ObservableObject` that wraps a target `Task` instance and enables the necessary notification logic for this method. The [`TaskNotifier`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject.TaskNotifier) type is also available to use directly if you have a general `Task` only. > [!NOTE] > The `SetPropertyAndNotifyOnCompletion` method is meant to replace the usage of the `NotifyTaskCompletion` type from the `Microsoft.Toolkit` package. If this type was being used, it can be replaced with just the inner `Task` (or `Task`) property, and then the `SetPropertyAndNotifyOnCompletion` method can be used to set its value and raise notification changes. All the properties exposed by the `NotifyTaskCompletion` type are available directly on `Task` instances. -## Sample Code +## Examples -There are more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). - -## Requirements - -| Device family | Universal, 10.0.16299.0 or higher | -| --- | --- | -| Namespace | Microsoft.Toolkit.Mvvm | -| NuGet package | [Microsoft.Toolkit.Mvvm](https://www.nuget.org/packages/Microsoft.Toolkit.Mvvm/) | - -## API - -* [ObservableObject source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableObject.cs) +You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). \ No newline at end of file diff --git a/docs/mvvm/ObservableRecipient.md b/docs/mvvm/ObservableRecipient.md index 1137879..50fdb7f 100644 --- a/docs/mvvm/ObservableRecipient.md +++ b/docs/mvvm/ObservableRecipient.md @@ -9,7 +9,9 @@ dev_langs: # ObservableRecipient -The [`ObservableRecipient`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.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. +The [`ObservableRecipient`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableRecipient) type is a base class for observable objects that also acts as recipients for messages. This class is an extension of [`ObservableObject`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject) which also provides built-in support to use the [`IMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.IMessenger) type. + +> **Platform APIs:** [`ObservableRecipient`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableRecipient), [`ObservableObject`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.componentmodel.ObservableObject), [`IMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.IMessenger), [`WeakReferenceMessenger`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.WeakReferenceMessenger), [`IRecipient`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.irecipient-1), [`PropertyChangedMessage`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.Messaging.Messages.PropertyChangedMessage-1) ## How it works @@ -55,17 +57,6 @@ public class MyViewModel : ObservableRecipient } ``` -## Sample Code +## Examples -There are more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). - -## Requirements - -| Device family | Universal, 10.0.16299.0 or higher | -| --- | --- | -| Namespace | Microsoft.Toolkit.Mvvm | -| NuGet package | [Microsoft.Toolkit.Mvvm](https://www.nuget.org/packages/Microsoft.Toolkit.Mvvm/) | - -## API - -* [ObservableRecipient source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableRecipient.cs) +You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). \ No newline at end of file diff --git a/docs/mvvm/RelayCommand.md b/docs/mvvm/RelayCommand.md index 8e123f6..0133afc 100644 --- a/docs/mvvm/RelayCommand.md +++ b/docs/mvvm/RelayCommand.md @@ -11,12 +11,14 @@ dev_langs: The [`RelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.RelayCommand) and [`RelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.RelayCommand-1) are `ICommand` implementations that can expose a method or delegate to the view. These types act as a way to bind commands between the viewmodel and UI elements. +> **Platform APIs:** [`RelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.RelayCommand), [`RelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.RelayCommand-1), [`IRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IRelayCommand), [`IRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IRelayCommand-1) + ## How they work `RelayCommand` and `RelayCommand` have the following main features: - They provide a base implementation of the `ICommand` interface. -- They also implement the `IRelayCommand` (and `IRelayCommand`) interface, which exposes a `NotifyCanExecuteChanged` method to raise the `CanExecuteChanged` event. +- They also implement the [`IRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IRelayCommand) (and [`IRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.input.IRelayCommand-1)) interface, which exposes a `NotifyCanExecuteChanged` method to raise the `CanExecuteChanged` event. - They expose constructors taking delegates like `Action` and `Func`, which allow the wrapping of standard methods and lambda expressions. ## Working with `ICommand` @@ -66,18 +68,6 @@ And the relative UI could then be (using WinUI XAML): The `Button` binds to the `ICommand` in the viewmodel, which wraps the private `IncrementCounter` method. The `TextBlock` displays the value of the `Counter` property and is updated every time the property value changes. -## Sample Code +## Examples -There are more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). - -## Requirements - -| Device family | Universal, 10.0.16299.0 or higher | -| --- | --- | -| Namespace | Microsoft.Toolkit.Mvvm | -| NuGet package | [Microsoft.Toolkit.Mvvm](https://www.nuget.org/packages/Microsoft.Toolkit.Mvvm/) | - -## API - -* [RelayCommand source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/Input/RelayCommand.cs) -* [RelayCommand<T> source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.Mvvm/Input/RelayCommand{T}.cs) +You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.Shared/Mvvm). \ No newline at end of file