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
Messenger.Register<PostWidgetViewModel, PropertyChangedMessage<object>>(this, (r, m) =>r.Receive(m));
140
140
}
141
141
142
142
/// <inheritdoc/>
@@ -156,9 +156,9 @@ We now have a draft of our viewmodels ready, and we can start looking into the s
156
156
## Building the settings service
157
157
158
158
> [!NOTE]
159
-
> The sample is built using the service locator pattern, but this is not the only possible pattern to use to manage service. The MVVM Toolkit also fully supports the dependency injection pattern, and you can choose the one you prefer depending on the architecture of your application, the available development time or personal preference.
159
+
> The sample is built using the dependency injection pattern, which is the recommended approach to deal with services in viewmodels. It is also possible to use other patterns, such as the service locator pattern, but the MVVM Toolkit does not offer built-in APIs to enable that.
160
160
161
-
Since we want some of our properties to be saved and persisted, we need a way for viewmodels to be able to interact with the application settings. We shouldn't use platform-specific APIs directly in our viewmodels though, as that would prevent us from having all our viewmodels in a portable, .NET Standard project. We can solve this issue by using services, and the `Ioc` class. The idea is to write interfaces that represent all the API surface that we need, and then to implement platform-specific types implementing this interface on all our application targets. The viewmodels will only interact with the interfaces, so they will not have any strong reference to any platform-specific type at all.
161
+
Since we want some of our properties to be saved and persisted, we need a way for viewmodels to be able to interact with the application settings. We shouldn't use platform-specific APIs directly in our viewmodels though, as that would prevent us from having all our viewmodels in a portable, .NET Standard project. We can solve this issue by using services, and the APIs in the `Microsoft.Extensions.DependencyInjection` library to setup our `IServiceProvider` instance for the application. The idea is to write interfaces that represent all the API surface that we need, and then to implement platform-specific types implementing this interface on all our application targets. The viewmodels will only interact with the interfaces, so they will not have any strong reference to any platform-specific type at all.
162
162
163
163
Here's a simple interface for a settings service:
164
164
@@ -189,14 +189,16 @@ We can assume that platform-specific types implementing this interface will take
189
189
/// <summary>
190
190
/// Gets the <seecref="ISettingsService"/> instance to use.
@@ -216,7 +218,7 @@ public string SelectedSubreddit
216
218
}
217
219
```
218
220
219
-
Here we're using the service locator pattern, which is one of the supported patterns by the `Ioc` class. We've declared an `ISettingsService SettingsService` field that just stores our settings service (we're retrieving it from the static `Ioc.Default` instance), and then we're initializing the `SelectedSubreddit` property in the constructor, by either using the previous value or just the first available subreddit. Then we also modified the `SelectedSubreddit` setter, so that it will also use the settings service to save the new value to disk.
221
+
Here we're using dependency injection and constructor injection, as mentioned above. We've declared an `ISettingsService SettingsService` field that just stores our settings service (which we're receiving as parameter in the viewmodel constructor), and then we're initializing the `SelectedSubreddit` property in the constructor, by either using the previous value or just the first available subreddit. Then we also modified the `SelectedSubreddit` setter, so that it will also use the settings service to save the new value to disk.
220
222
221
223
Great! Now we just need to write a platform specific version of this service, this time directly inside one of our app projects. Here's what that service might look like on UWP:
222
224
@@ -248,16 +250,29 @@ public sealed class SettingsService : ISettingsService
248
250
}
249
251
```
250
252
251
-
The final piece of the puzzle is to inject this platform-specific service into our service provider instance, which in this case is the `Ioc.Default` instance. We can do this at startup, like so:
253
+
The final piece of the puzzle is to inject this platform-specific service into our service provider instance. We can do this at startup, like so:
252
254
253
255
```csharp
254
-
Ioc.Default.ConfigureServices(services=>
256
+
/// <summary>
257
+
/// Gets the <seecref="IServiceProvider"/> instance to resolve application services.
This will register a singleton instance of our `SettingsService` as a type implementing `ISettingsService`. This means that every time one of our viewmodels uses `Ioc.Default.GetService<ISettingsService>()` while the app in use is the UWP one, it will receive a `SettingsService` instance, which will use the UWP APIs behind the scene to manipulate settings. Perfect!
275
+
This will register a singleton instance of our `SettingsService` as a type implementing `ISettingsService`. We are also registering the `PostWidgetViewModel` as a transient service, meaning every time we retrieve an instance, it will be a new one (you can imagine this being useful if wanted to have multiple, independent post widgets). This means that every time we resolve an `ISettingsService` instance while the app in use is the UWP one, it will receive a `SettingsService` instance, which will use the UWP APIs behind the scene to manipulate settings. Perfect!
261
276
262
277
## Building the Reddit service
263
278
@@ -329,11 +344,19 @@ We have added a new `IRedditService` field to store our service, just like we di
329
344
The last missing piece now is just to inject the actual service into our service provider. The big difference in this case is that by using `refit` we don't actually need to implement the service at all! The library will automatically create a type implementing the service for us, behind the scenes. So we only need to get an `IRedditService` instance and inject it directly, like so:
And that's all we need to do! We now have all our backend ready to use, including two custom services that we created specifically for this app! 🎉
@@ -342,6 +365,20 @@ And that's all we need to do! We now have all our backend ready to use, includin
342
365
343
366
Now that all the backend is completed, we can write the UI for our widgets. Note how using the MVVM pattern let us focus exclusively on the business logic at first, without having to write any UI-related code until now. Here we'll remove all the UI code that's not interacting with our viewmodels, for simplicity, and we'll go through each different control one by one. The full source code can be found in the sample app.
344
367
368
+
Before going through the various controls, here's how we can resolve viewmodels for all the different views in our application (eg. the `PostWidgetView`):
We're using our `IServiceProvider` instance to resolve the `PostWidgetViewModel` object we need, which is then assigned to the data context property. We're also creating a strongly-typed `ViewModel` property that simply casts the data context to the correct viewmodel type - this is needed to enable `x:Bind` in the XAML code.
381
+
345
382
Let's start with the subreddit widget, which features a `ComboBox` to select a subreddit, a `Button` to refresh the feed, a `ListView` to display posts and a `ProgressBar` to indicate when the feed is loading. We'll assume that the `ViewModel` property represents an instance of the viewmodel we've described before - this can be declared either in XAML or directly in code behind.
0 commit comments