Skip to content

Commit 2d3f1cd

Browse files
committed
docs: Add RouteChange Event Tutorial
1 parent d515170 commit 2d3f1cd

File tree

3 files changed

+295
-2
lines changed

3 files changed

+295
-2
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
---
2+
uid: DevTKSS.Uno.ExtensionsNavigation.HowTo-ChangeRoutes.de
3+
---
4+
5+
# Anleitung: Reagieren auf Routen Änderungen
6+
7+
Wenn wir nun Routen in unserer Anwendung registriert haben, möchten wir vielleicht auch auf Änderungen der Route reagieren, um beispielsweise bestimmte Aktionen auszuführen oder Daten zu laden.
8+
9+
Dafür können wir den `IRouteNotifier` nutzen, um uns über Änderungen der aktuellen Route zu informieren. Dieser enthält das Ereignis `RouteChanged`, das ausgelöst wird, wenn sich die Route ändert. Im folgenden Beispiel werde ich dir zeigen, wie du dies in deiner Anwendung implementieren kannst.
10+
11+
## Voraussetzungen
12+
13+
Bevor du mit der Implementierung beginnst, benötigst du zum einen natürlich das Uno Feature "Navigation" in deiner Anwendung, also die `Uno.Extensions.Navigation` Bibliothek, zum anderen aber auch eine oder mehrere [Routen registriert](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-RegisterRoutes.de) haben und eine ViewModel oder Model Klasse mit zugehöriger Seite, also View erzeugt haben und dort navigieren können. Der Einfachheit halber werde ich davon ausgehen, dass du hierfür die folgenden drei Tutorials bereits erfolgreich absolviert hast:
14+
15+
- [Tutorial: Erstellen des UI mit einer `NavigationView` in Xaml](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-Defining-UI.de)
16+
- [Tutorial: Routen in der App registrieren](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-RegisterRoutes.de)
17+
- [Tutorial: Anleitung: Navigation im Model oder ViewModel](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-DefiningModelOrViewModel.de)
18+
19+
## Tutorial Video: Reagieren auf Routen Änderungen
20+
21+
In diesem Video werden wir uns zusammen anschauen, wie du auf Routen Änderungen in deiner Anwendung reagieren kannst, indem du den `IRouteNotifier` verwendest. Wir werden sehen, wie du das Ereignis `RouteChanged` abonnierst und darauf reagierst. Den Code kannst du dabei direkt aus dem Code hierunter kopieren und in deine Anwendung einfügen, wenn du möchtest, aber aus eigener Erfahrung heraus hilft es dir mehr, den Code selber zu schreiben und dabei zuzuschauen, wie es funktioniert. So kannst du auch besser verstehen, was du tust und warum.
22+
23+
![Reagieren-auf-Routen-aenderungen-mit-IRouteNotifier](https://www.youtube.com/embed/RZ3RirA7jhk)
24+
25+
## IRouteNotifier im Model Konstruktor anfordern
26+
27+
Um den `IRouteNotifier` in deinem Model oder ViewModel zu verwenden, musst du ihn als Abhängigkeit im Konstruktor anfordern. Hier ist ein Beispiel, wie du dies in einem ViewModel machen kannst:
28+
29+
```csharp
30+
public partial record MainModel
31+
{
32+
private readonly IRouteNotifier _routeNotifier;
33+
34+
public MainModel(IRouteNotifier routeNotifier)
35+
{
36+
_routeNotifier = routeNotifier;
37+
_routeNotifier.RouteChanged += OnRouteChanged;
38+
}
39+
40+
private void OnRouteChanged(object sender, RouteChangedEventArgs e)
41+
{
42+
// Hier kannst du später auf die Routenänderung reagieren
43+
}
44+
}
45+
```
46+
47+
> [!IMPORTANT]
48+
> Am verlässlichsten aus meiner Erfahrung funktioniert es, wenn wir das Ereignis in eine eigene Methode auslagern, also nicht als Lambda Ausdruck im Konstruktor registrieren. Eigentlich sollte zwar das selbe passieren, aber ich hatte öfters das Problem, dass die Methode nicht aufgerufen wurde, wenn ich es als Lambda Ausdruck geschrieben habe.
49+
50+
## Verknüpfen des Routen Namens mit einem `IState<string>` im Model
51+
52+
Um den aktuellen Routen Namen in deinem Model oder ViewModel zu speichern und darauf zuzugreifen, kannst du ein `IState<string>` verwenden. Hier ist ein Beispiel, wie du dies implementieren kannst:
53+
54+
```csharp
55+
public IState<string> Title => State<string>.Value(this, () => "Dashboard");
56+
```
57+
58+
Nun ist der aktuelle Inhalt natürlich hardcoded. Was wenn wir uns aber zwischendurch doch mal umentscheiden und die Seite umbenennen? Und wollen wir denn überhaupt dass der Titel, in dem Fall unser Routen Name gerade, abhängig davon ist, wie unser Model heißt? Gehen wir mal davon aus, dass dem nicht so ist.
59+
60+
1. Wollen wir also nun als initialen Wert erstmal den aktuellen Routen Namen verwenden ohne den Namen hart zu codieren, brauchen wir erstmal einen `INavigator`, falls wir den noch nicht zuvor angefordert haben:
61+
62+
```diff
63+
public partial record MainModel
64+
{
65+
private readonly IRouteNotifier _routeNotifier;
66+
+ private readonly INavigator _navigator;
67+
68+
public MainModel(
69+
IRouteNotifier routeNotifier,
70+
+ INavigator navigator)
71+
{
72+
_routeNotifier = routeNotifier;
73+
_routeNotifier.RouteChanged += Main_OnRouteChanged;
74+
+ _navigator = navigator;
75+
}
76+
}
77+
```
78+
79+
2. Und danach können wir dann hiermit auch direkt unsere aktuelle Route abfragen, auf der wir uns befinden:
80+
81+
```csharp
82+
public IState<string> Title => State<string>.Value(this, () => _navigator.Route?.ToString() ?? string.Empty);
83+
```
84+
85+
**Was passiert hier?**
86+
Was wir effektiv nun geändert haben, ist dass wir nun:
87+
1. Von unserem Navigator die Eigenschaft `Route` abfragen, die uns das aktuelle `Route`-Objekt oder (laut Compiler) möglicherweise auch `null` zurückgibt.
88+
2. Der `Route`-Type hat eine Überschreibung der `ToString()` Methode, welche uns bei Abfrage dann den Routen-Namen als `string` zurückgibt.
89+
3. Falls dieser Wert selber oder eben infolge von einem `null` Wert der `Route` Eigenschaft `null` sein sollte, geben wir stattdessen einen leeren String zurück mittels des Null-Koaleszenz Operators `??`.
90+
91+
Und somit haben wir auch schon unseren aktuellen Routen Namen griffbereit hinterlegt in unserem Model im `Title`.
92+
93+
Dort im Model selber bringt unserem User das aber natürlich erstmal nichts, also müssen wir das Ganze ja auch noch irgendwie in der UI anzeigen. Erinnerst du dich noch daran, dass ich im [Tutorial: Erstellen des UI mit einer `NavigationView` in Xaml](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-Defining-UI.de) gezeigt habe, wie du den Header der `NavigationView` an eine Eigenschaft im Model binden kannst? Zufälligerweise hieß diese Eigenschaft genau `Title`. Perfekt!
94+
95+
Solltest du das noch nicht gemacht haben, dann geht das jetzt ganz einfach auch noch nachträglich:
96+
97+
```diff
98+
<NavigationView uen:Region.Attached="True"
99+
+ Header="{Binding Title}"
100+
IsPaneToggleButtonVisible="True"
101+
PaneDisplayMode="Auto">
102+
<NavigationView.MenuItems>
103+
<NavigationViewItem Content="Home"
104+
uen:Region.Name="Dashboard"
105+
Icon="Home" />
106+
<NavigationViewItem Content="Some View"
107+
uen:Region.Name="Second"
108+
Icon="AddFriend" />
109+
</NavigationView.MenuItems>
110+
<NavigationView.Content>
111+
<Grid uen:Region.Attached="True" />
112+
</NavigationView.Content>
113+
</NavigationView>
114+
```
115+
116+
## Reagieren auf Routen Änderungen
117+
118+
Nun fehlt nur noch die eigentliche Reaktion auf die Routen Änderung. Das machen wir in der Methode `OnRouteChanged`, die wir zuvor im Konstruktor registriert haben. Hier ist ein Beispiel, wie du dies implementieren kannst:
119+
120+
[!code-csharp[](../../../../src/DevTKSS.Uno.XamlNavigationApp-1/Presentation/MainModel.cs#L19-L22)]
121+
122+
**Was passiert hier?**
123+
124+
1. Ein `IState<T>` hat die Methode `SetAsync`, mit der wir den Wert des States ändern können. Diese ist asynchron, daher verwenden wir das `await` Schlüsselwort davor.
125+
2. In den RouteChangedEventArgs `e` erhalten wir den `Navigator`, der wie schon zuvor beim [Verknüpfen des Routen Namens](#verknüpfen-des-routen-namens-mit-einem-istatestring-im-model) in der Überladung der `Route.ToString()` Methode den aktuellen Routen Namen als `string` zurückgibt.
126+
3. Diesen übergeben wir dann direkt an die `SetAsync` Methode und lassen das Mvux für uns die Aktualisierung des States und somit auch der UI übernehmen.
127+
128+
> [!TIP]
129+
> Ist dir aufgefallen, dass wir hier beim EventHandler `OnRouteChanged` `async void` verwenden mussten? Das liegt daran, dass EventHandler immer `void` zurückgeben müssen. In solchen Fällen ist es üblich, `async void` zu verwenden, um asynchrone Operationen durchzuführen. Allerdings solltest du `async void` nur in solchen Fällen mit EventHandlern verwenden und sonst immer `async Task` oder `ValueTask` bevorzugen, um eine bessere Fehlerbehandlung und Kontrolle über asynchrone Operationen zu haben. Ohne das `async` Schlüsselwort könnten wir das `await` nicht verwenden, was uns hier aber zwingend notwendig ist, um die asynchrone Methode `SetAsync` aufzurufen ohne vom Compiler wiederum die Meldung zu bekommen, dass wir `await` nutzen sollten.
130+
131+
## Anwendung starten
132+
133+
Nun sind wir auch schon fertig mit dem erstellen unseres Codes und können die Anwendung einfach mal starten.
134+
135+
Wenn du nun in der NavigationView auf einen anderen Menüpunkt klickst und korrekt zuvor die Routen registriert hast, solltest du sehen, dass sich der Text im Header der NavigationView entsprechend ändert und den aktuellen Routen Namen anzeigt.
136+
137+
Glückwunsch! Du hast erfolgreich gelernt, wie du auf Routen Änderungen in deiner Anwendung reagieren kannst, indem du den `IRouteNotifier` verwendest und den aktuellen Routen Namen in deinem Model speicherst.
138+
139+
**Hier nochmal der komplette Code, den du in deinem Model von diesem Tutorial haben solltest:**
140+
141+
[!code-csharp[](../../../../src/DevTKSS.Uno.XamlNavigationApp-1/Presentation/MainModel.cs#L3-L25)]
142+
143+
## Links zur Uno Documentation
144+
145+
- [IRouteNotifier Documentation](https://platform.uno/docs/articles/external/uno.extensions/doc/Learn/Navigation/Advanced/HowTo-IRouteNotifier.html)

doc/articles/de/toc.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
uid: DevTKSS.Uno.ResourcesLookup.de
77
href: Uno-resources-lookup-de.md
88
- name: Mvux Gallery Übersicht
9-
uid: DevTKSS.Uno.Samples.MvuxGallery.Overview.de
9+
uid: DevTKSS.Uno.SampleApps.MvuxGallery.Overview.de
1010
href: MvuxGallery-Overview-de.md
1111
- name: "Erste Schritte & Basics"
1212
items:
@@ -17,7 +17,7 @@
1717
uid: DevTKSS.Uno.Setup.HowTo-CreateNewUnoApp.de
1818
href: HowTo-CreateApp-de.md
1919
- name: "Hinzufügen von Seiten"
20-
uid: DevTKSS.Uno.Setup.AddingNewPages.de
20+
uid: DevTKSS.Uno.Setup.HowTo-AddingNewPages.de
2121
href: HowTo-Adding-New-Pages-de.md
2222
- name: "Neue Klassen oder Record ViewModel's definieren"
2323
uid: DevTKSS.Uno.Setup.HowTo-AddingNew-VM-Class-Record.de
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
---
2+
uid: DevTKSS.Uno.ExtensionsNavigation.HowTo-ChangeRoutes.en
3+
---
4+
5+
# How-To: React to Route Changes
6+
7+
If we now have routes registered in our application, we may also want to react to changes in the route, for example to execute certain actions or load data.
8+
9+
For this, we can use the `IRouteNotifier` to inform us about changes to the current route. It contains the `RouteChanged` event, which is triggered when the route changes. In the following example, I will show you how to implement this in your application.
10+
11+
## Prerequisites
12+
13+
Before you start with the implementation, you need the Uno feature "Navigation" in your application, i.e. the `Uno.Extensions.Navigation` library, but also have one or more [routes registered](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-RegisterRoutes.en) and have created a ViewModel or Model class with an associated page, i.e. View, and be able to navigate there. For simplicity, I will assume that you have already successfully completed the following three tutorials:
14+
15+
- [Tutorial: Creating the UI with a `NavigationView` in Xaml](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-Defining-UI.en)
16+
- [Tutorial: Registering Routes in the App](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-RegisterRoutes.en)
17+
- [Tutorial: Navigation in the Model or ViewModel](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-DefiningModelOrViewModel.en)
18+
19+
## Tutorial Video: Reacting to Route Changes
20+
21+
In this video, we will look at how you can react to route changes in your application by using the `IRouteNotifier`. We will see how to subscribe to the `RouteChanged` event and respond to it. You can copy the code directly from the code below and paste it into your application if you want, but from my own experience, it helps you more to write the code yourself and watch how it works. This way you can also better understand what you are doing and why.
22+
23+
> [!NOTE]
24+
> This video is currently only available in German, but transcriptions have been added to the video description, which should be usable through YouTube's auto-translate feature. You can also enable auto-translated subtitles in YouTube to follow along in your preferred language.
25+
26+
![Responding-to-Route-Changes-with-IRouteNotifier](https://www.youtube.com/embed/RZ3RirA7jhk)
27+
28+
## Requesting IRouteNotifier in the Model Constructor
29+
30+
To use the `IRouteNotifier` in your Model or ViewModel, you need to request it as a dependency in the constructor. Here is an example of how you can do this in a ViewModel:
31+
32+
```csharp
33+
public partial record MainModel
34+
{
35+
private readonly IRouteNotifier _routeNotifier;
36+
37+
public MainModel(IRouteNotifier routeNotifier)
38+
{
39+
_routeNotifier = routeNotifier;
40+
_routeNotifier.RouteChanged += OnRouteChanged;
41+
}
42+
43+
private void OnRouteChanged(object sender, RouteChangedEventArgs e)
44+
{
45+
// Here you can later react to the route change
46+
}
47+
}
48+
```
49+
50+
> [!IMPORTANT]
51+
> In my experience, the most reliable approach is to extract the event into a separate method, rather than registering it as a lambda expression in the constructor. Although the same thing should happen, I often had the problem that the method was not called when I wrote it as a lambda expression.
52+
53+
## Linking the Route Name with an `IState<string>` in the Model
54+
55+
To store and access the current route name in your Model or ViewModel, you can use an `IState<string>`. Here is an example of how you can implement this:
56+
57+
```csharp
58+
public IState<string> Title => State<string>.Value(() => "Dashboard");
59+
```
60+
61+
Now the current content is hardcoded, of course. But what if we change our mind in the meantime and rename the page? And do we even want the title, in this case our current route name, to depend on what our Model is called? Let's assume that this is not the case.
62+
63+
1. So, if we want to use the current route name as the initial value without hardcoding the name, we first need an `INavigator`, if we haven't requested it before:
64+
65+
```diff
66+
public partial record MainModel
67+
{
68+
private readonly IRouteNotifier _routeNotifier;
69+
+ private readonly INavigator _navigator;
70+
71+
public MainModel(
72+
IRouteNotifier routeNotifier,
73+
+ INavigator navigator)
74+
{
75+
_routeNotifier = routeNotifier;
76+
_routeNotifier.RouteChanged += Main_OnRouteChanged;
77+
+ _navigator = navigator;
78+
}
79+
}
80+
```
81+
82+
2. And then we can directly query our current route on which we are located:
83+
84+
```csharp
85+
public IState<string> Title => State<string>.Value(this, () => _navigator.Route?.ToString() ?? string.Empty);
86+
```
87+
88+
**What happens here?**
89+
What we have effectively changed now is that we now:
90+
1. Query the `Route` property from our Navigator, which returns the current `Route` object or (according to the compiler) possibly also `null`.
91+
2. The `Route` type has an override of the `ToString()` method, which returns the route name as a `string` when queried.
92+
3. If this value itself or as a result of a `null` value of the `Route` property should be `null`, we return an empty string instead using the null-coalescing operator `??`.
93+
94+
And so we already have our current route name readily stored in our Model in the `Title`.
95+
96+
But of course, this doesn't help our user in the Model itself, so we still have to display it somehow in the UI. Do you remember that I showed you in the [Tutorial: Creating the UI with a `NavigationView` in Xaml](xref:DevTKSS.Uno.ExtensionsNavigation.HowTo-Defining-UI.en) how to bind the header of the `NavigationView` to a property in the Model? Coincidentally, this property was called exactly `Title`. Perfect!
97+
98+
If you haven't done this yet, it's very easy to do it afterwards:
99+
100+
```diff
101+
<NavigationView uen:Region.Attached="True"
102+
+ Header="{Binding Title}"
103+
IsPaneToggleButtonVisible="True"
104+
PaneDisplayMode="Auto">
105+
<NavigationView.MenuItems>
106+
<NavigationViewItem Content="Home"
107+
uen:Region.Name="Dashboard"
108+
Icon="Home" />
109+
<NavigationViewItem Content="Some View"
110+
uen:Region.Name="Second"
111+
Icon="AddFriend" />
112+
</NavigationView.MenuItems>
113+
<NavigationView.Content>
114+
<Grid uen:Region.Attached="True" />
115+
</NavigationView.Content>
116+
</NavigationView>
117+
```
118+
119+
## Reacting to Route Changes
120+
121+
Now we only need the actual reaction to the route change. We do this in the `OnRouteChanged` method, which we registered in the constructor earlier. Here is an example of how you can implement this:
122+
123+
[!code-csharp[](../../../../src/DevTKSS.Uno.XamlNavigationApp-1/Presentation/MainModel.cs#L19-L22)]
124+
125+
**What happens here?**
126+
127+
1. An `IState<T>` has the `SetAsync` method, with which we can change the value of the state. This is asynchronous, so we use the `await` keyword before it.
128+
2. In the RouteChangedEventArgs `e` we get the `Navigator`, which, as before when [linking the route name](#linking-the-route-name-with-an-istatestring-in-the-model), returns the current route name as a `string` in the overload of the `Route.ToString()` method.
129+
3. We then pass this directly to the `SetAsync` method and let Mvux handle the update of the state and thus also the UI for us.
130+
131+
> [!TIP]
132+
> Did you notice that we had to use `async void` here for the EventHandler `OnRouteChanged`? This is because EventHandlers must always return `void`. In such cases, it is common to use `async void` to perform asynchronous operations. However, you should only use `async void` in such cases with EventHandlers and otherwise always prefer `async Task` or `ValueTask` for better error handling and control over asynchronous operations. Without the `async` keyword, we couldn't use `await`, which is absolutely necessary here to call the asynchronous method `SetAsync` without getting a message from the compiler that we should use `await`.
133+
134+
## Starting the Application
135+
136+
Now we are done creating our code and can simply start the application.
137+
138+
If you now click on another menu item in the NavigationView and have correctly registered the routes beforehand, you should see that the text in the header of the NavigationView changes accordingly and displays the current route name.
139+
140+
Congratulations! You have successfully learned how to respond to route changes in your application by using the `IRouteNotifier` and storing the current route name in your Model.
141+
142+
**Here is the complete code you should have in your Model from this tutorial:**
143+
144+
[!code-csharp[](../../../../src/DevTKSS.Uno.XamlNavigationApp-1/Presentation/MainModel.cs#L3-L25)]
145+
146+
## Links to Uno Documentation
147+
148+
- [IRouteNotifier Documentation](https://platform.uno/docs/articles/external/uno.extensions/doc/Learn/Navigation/Advanced/HowTo-IRouteNotifier.html)

0 commit comments

Comments
 (0)