You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
🆕 New `ObservableValidator` class, which supports the [`INotifyDataErrorInfo`](https://docs.microsoft.com/dotnet/api/system.componentmodel.inotifydataerrorinfo) interface.
4
+
5
+
🆕 Added new constructors with cancellation support to the async commands, and added new cancellation-related properties to the async command interfaces.
6
+
7
+
🆕 Added a new `WeakReferenceMessenger` type. This type is less performant than the other messenger, and uses more memory, and in return only uses weak references to track recipients. This type essentially mirrors the behavior of the `Messenger` type from `MvvmLight`, making the transition easier for developers migrating from that library.
8
+
9
+
🆕 Introduced a new custom delegate to represent message handlers, which also receives the current recipient as additional input parameter (💥 breaking change, see code changes below).
10
+
11
+
✅ Renamed `Messenger` to `StrongReferenceMessenger` (💥).
12
+
13
+
✅ The `WeakReferenceMessenger` is now the default messenger used by the `ObservableRecipient` class (💥).
14
+
15
+
✅ Changed `ObservableObject` overloads using `Expression<Func<T>>` to be more efficient (💥).
16
+
17
+
✅ API changes to the `SetPropertyAndNotifyOnCompletion` (as detailed in [this blog post](https://devblogs.microsoft.com/pax-windows/mvvm-toolkit-preview-3-the-journey-of-an-api/), 💥).
18
+
19
+
🚨 Removed the `Ioc` class (we will include docs on how to easily start using the `Microsoft.Extensions.DependencyInjection` library directly to work with dependency injection, 💥).
20
+
21
+
## Breaking changes
22
+
23
+
💥 If you were registering message handlers, no API changes are required for messages registered through the `IRecipient<TMessage>` interface. If you were manually registering handlers with the `Action<TMessage>` delegate instead, you will need to modify their code as follows:
24
+
25
+
```cs
26
+
// Preview 2
27
+
Messenger.Register<MyMessage>(this, message=>
28
+
{
29
+
// Do stuff with the message here...
30
+
// Note that invoking this instance method means that
31
+
// the lambda expression is also capturing "this".
// Note that since we're accessing the recipient from the
41
+
// input parameter, the lambda expression is not capturing
42
+
// anything anymore, which allows the C# compiler to cache it.
43
+
// Note that we can still access all private members of the
44
+
// recipient from here, even if we're using the input parameter.
45
+
recipient.SomeInstanceMethod();
46
+
});
47
+
```
48
+
49
+
💥 If you were directly referencing `Messenger.Default` to send messages (ie. outside of the `ObservableRecipient` class, which exposes a `Messenger` property which is unchanged), you'll need to replace that with either `WeakReferenceMessenger.Default` or `StrongReferenceMessenger.Default`, depending on the desired messenger to use.
50
+
51
+
💥 If you want to use the `StrongReferenceMessenger` class for better performance, make sure to pass that to the constructor of the `ObservableRecipient` class, otherwise the `WeakReferenceMessenger.Default` instance will be used.
52
+
53
+
💥 If you were using the `ObservableObject.SetProperty<T>(Expression<Func<T>>, ...)` overload, the code needs to be updated as follows to replace the LINQ expression with a stateless lambda expression:
The syntax is slightly more complex, but results in a 150x speed improvement (that's not a typo), requires no memory allocations at all and no reflection, and ensures that all necessary validation of the arguments can be done at compile time too.
69
+
70
+
💥 If you were using `SetPropertyAndNotifyOnCompletion`, change the code as follows:
💥 If you were using the `Ioc` class, we will provide docs to illustrate how to setup a custom service container using the `Microsoft.Extensions.DependencyInjection` library soon. The temporary link with the preview docs for this is available [here](https://github.com/windows-toolkit/MVVM-Samples/blob/feature/preview2-update/docs/mvvm/Ioc.md).
93
+
94
+
## Notes
95
+
96
+
For additional info and discussion, see the related issue [here](https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3428).
Copy file name to clipboardExpand all lines: docs/mvvm/AsyncRelayCommand.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,7 +15,8 @@ The [`AsyncRelayCommand`](https://docs.microsoft.com/dotnet/api/microsoft.toolki
15
15
16
16
`AsyncRelayCommand` and `AsyncRelayCommand<T>` have the following main features:
17
17
18
-
- They extend the functionalities of the non-asynchronous commands included in the library, with support for `Task`-returning delegates.
18
+
- They extend the functionalities of the synchronous commands included in the library, with support for `Task`-returning delegates.
19
+
- 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.
19
20
- 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.
20
21
- They implement the `IAsyncRelayCommand` and `IAsyncRelayCommand<T>` 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.
Copy file name to clipboardExpand all lines: docs/mvvm/Ioc.md
+98-24Lines changed: 98 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,47 +1,82 @@
1
1
---
2
2
title: Ioc
3
3
author: Sergio0694
4
-
description: A type that facilitates the use of the IServiceProvider type
4
+
description: An introduction to the use of the IServiceProvider type through the Microsoft.Extensions.DependencyInjection APIs
5
5
keywords: windows 10, uwp, windows community toolkit, uwp community toolkit, uwp toolkit, mvvm, service, dependency injection, net core, net standard
6
6
dev_langs:
7
7
- csharp
8
8
---
9
9
10
10
# Ioc ([Inversion of control](https://en.wikipedia.org/wiki/Inversion_of_control))
11
11
12
-
The [`Ioc`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.mvvm.DependencyInjection.Ioc) class is a type that facilitates the use of the `IServiceProvider` type. It's powered by 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`.
12
+
A common pattern that can be used to increase modularity in the codebase of an application using the MVVM pattern is to use some form of inversion of control. One of the most common solution in particular is to use dependency injection, which consists in creating a number of services that are injected into backend classes (ie. passed as parameters to the viewmodel constructors) - this allows code using these services not to rely on implementation details of these services, and it also makes it easy to swap the concrete implementations of these services. This pattern also makes it easy to make platform-specific features available to backend code, by abstracting them through a service which is then injected where needed.
13
+
14
+
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.
13
15
14
16
## Configure and resolve services
15
17
16
-
The main entry point is the `ConfigureServices` method, which can be used like so:
18
+
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):
The `Ioc.Default` property offers a thread-safe `IServiceProvider` instance that can be used anywhere in the application to resolve services. The `ConfigureService` method handles the initialization of that service. It is also possible to create different `Ioc` instances and to initialize each with different services.
58
+
Here the `Services` property is initialized at startup, and all the application services and viewmodels are registered. There is also a new `Current` property that can be used to easily access the `Services` property from other views in the application. For instance:
The key aspect here is that each service may very well be using platform-specific APIs, but since those are all abstracted away through the interface our code is using, we don't need to worry about them whenever we're just resolving an instance and using it to perform operations.
32
67
33
68
## Constructor injection
34
69
35
70
One powerful feature that is available is "constructor injection", which means that the DI service provider is able to automatically resolve indirect dependencies between registered services when creating instances of the type being requested. Consider the following service:
36
71
37
72
```csharp
38
-
publicclassConsoleLogger : ILogger
73
+
publicclassFileLogger : IFileLogger
39
74
{
40
-
privatereadonlyIFileServiceFileService;
75
+
privatereadonlyIFilesServiceFileService;
41
76
privatereadonlyIConsoleServiceConsoleService;
42
77
43
-
publicConsoleLogger(
44
-
IFileServicefileService,
78
+
publicFileLogger(
79
+
IFilesServicefileService,
45
80
IConsoleServiceconsoleService)
46
81
{
47
82
FileService=fileService;
@@ -52,22 +87,61 @@ public class ConsoleLogger : ILogger
52
87
}
53
88
```
54
89
55
-
Here we have a `ConsoleLogger`implementing the `ILogger` interface, and requiring `IFileService` and `IConsoleService` instances. Constructor injection means the DI service provider will "automagically" gather all the necessary services, like so:
90
+
Here we have a `FileLogger` type implementing the `IFileLogger` interface, and requiring `IFilesService` and `IConsoleService` instances. Constructor injection means the DI service provider will automatically gather all the necessary services, like so:
The DI service provider will automatically check whether all the necessary services are registered, then it will retrieve them and invoke the constructor for the registered `IFileLogger` concrete type, to get the instance to return.
112
+
113
+
## What about viewmodels?
114
+
115
+
A service provider has "service" in its name, but it can actually be used to resolve instances of any class, including viewmodels! The same concepts explained above still apply, including constructor injection. Imagine we had a `ContactsViewModel` type, using an `IContactsService` and an `IPhoneService` instance through its constructor. We could have a `ConfigureServices` method like this:
The DI service provider will automatically check whether all the necessary services are registered, then it will retrieve them and invoke the constructor for the registered `ILogger` concrete type, to get the instance to return - all done automatically!
136
+
And then in our `ContactsView`, we would assign the data context as follows:
0 commit comments