Skip to content
This repository was archived by the owner on Nov 8, 2025. It is now read-only.

Commit b1faae9

Browse files
Update documentation and samples for .NET 10 and SmartNavigation rename (#74)
* Initial plan * Update README with SmartNavigation rename and new features Co-authored-by: matt-goldman <[email protected]> * Add NavigationManager and Lifecycle demo to showcase new features Co-authored-by: matt-goldman <[email protected]> * Update NuGet package description with new features Co-authored-by: matt-goldman <[email protected]> * Fix typo: aggreagate -> aggregate Co-authored-by: matt-goldman <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: matt-goldman <[email protected]>
1 parent 25a5c28 commit b1faae9

File tree

7 files changed

+366
-24
lines changed

7 files changed

+366
-24
lines changed

README.md

Lines changed: 192 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,141 @@
66
<img src="http://img.youtube.com/vi/qx8A4zIe9dU/hqdefault.jpg" alt="Watch the video" />
77
</a>
88

9-
# MAUI PageResolver
10-
A simple and lightweight page resolver for use in .NET MAUI projects.
9+
# MAUI Smart Navigation (formerly PageResolver)
1110

12-
If you want a simple page resolver with DI without using a full MVVM framework (or if you want to use MVU), this package will let you navigate to fully resolved pages, with view models and dependencies, by calling:
11+
> **📢 Important:** This library was renamed from `Plugin.Maui.PageResolver` to `Plugin.Maui.SmartNavigation` in v3.0 for .NET 10. See the [migration guide](#migrating-from-pageresolver-2x) below.
1312
14-
```cs
13+
A simple and lightweight navigation solution for .NET MAUI projects with dependency injection support.
14+
15+
If you want a simple navigation solution with DI without using a full MVVM framework (or if you want to use MVU), this package will let you navigate to fully resolved pages, with view models and dependencies.
16+
17+
## Quick Start Examples
18+
19+
### Basic Navigation
20+
21+
```csharp
22+
// Using INavigationManager (recommended)
23+
public class MyViewModel
24+
{
25+
private readonly INavigationManager _navigationManager;
26+
27+
public MyViewModel(INavigationManager navigationManager)
28+
{
29+
_navigationManager = navigationManager;
30+
}
31+
32+
public async Task NavigateToDetails()
33+
{
34+
await _navigationManager.PushAsync<DetailsPage>();
35+
}
36+
}
37+
38+
// Or using extension methods on INavigation
1539
await Navigation.PushAsync<MyPage>();
1640
```
1741

18-
# Advanced features
42+
### Navigation with Parameters
1943

20-
Additional features supported by PageReolver:
21-
* Paramaterised navigation - pass page parameters
44+
Pass parameters to pages or ViewModels:
2245

2346
```csharp
47+
// Page parameters
2448
await Navigation.PushAsync<MyPage>(myPageParam1, "bob", 4);
49+
50+
// ViewModel parameters
51+
await Navigation.PushAsync<MyPage>(myViewModelParam1, "bob", 4);
2552
```
2653

27-
* Paramaterised navigation - pass ViewModel parameters
54+
### Modal Navigation
2855

2956
```csharp
30-
await Navigation.PushAsync<MyPage>(myViewModelParam1, "bob", 4);
57+
await _navigationManager.PushModalAsync<SettingsPage>();
58+
await _navigationManager.PopModalAsync();
3159
```
3260

33-
* Source generator - automatically register dependencies in `IServiceCollection` with generated code
34-
61+
### Shell Navigation (Type-Safe Routing)
62+
63+
```csharp
64+
await _navigationManager.GoToAsync(new Route("details", typeof(DetailsPage)));
65+
```
66+
67+
### Go Back
68+
69+
```csharp
70+
// Automatically determines whether to pop modal, Shell, or navigation stack
71+
await _navigationManager.GoBackAsync();
72+
```
73+
74+
## Key Features
75+
76+
### INavigationManager Service
77+
A DI-friendly navigation service that works with Shell and non-Shell navigation:
78+
79+
```csharp
80+
public interface INavigationManager
81+
{
82+
Task GoToAsync(Route route, string? query = null);
83+
Task GoBackAsync();
84+
Task PushAsync<TPage>(object? args = null) where TPage : Page;
85+
Task PopAsync();
86+
Task PushModalAsync<TPage>(object? args = null) where TPage : Page;
87+
Task PopModalAsync();
88+
}
89+
```
90+
91+
### ViewModel Lifecycle with NavigatedInitBehavior
92+
93+
Implement `IViewModelLifecycle` in your ViewModels for async initialization:
94+
95+
```csharp
96+
public class MyViewModel : IViewModelLifecycle
97+
{
98+
public async Task OnInitAsync(bool isFirstNavigation)
99+
{
100+
// Perform async initialization
101+
if (isFirstNavigation)
102+
{
103+
// First-time setup
104+
await LoadDataAsync();
105+
}
106+
}
107+
}
108+
```
109+
110+
Attach the behavior in XAML:
111+
112+
```xml
113+
<ContentPage xmlns:behaviors="clr-namespace:Plugin.Maui.SmartNavigation.Behaviours;assembly=Plugin.Maui.SmartNavigation">
114+
<ContentPage.Behaviors>
115+
<behaviors:NavigatedInitBehavior />
116+
</ContentPage.Behaviors>
117+
</ContentPage>
118+
```
119+
120+
### Source Generator (Opt-In)
121+
122+
Automatically register dependencies in `IServiceCollection` with generated code. **Note:** The source generator is now **opt-in** in v3.0 (previously opt-out).
123+
124+
To enable, add the `[GenerateAutoDependencies]` attribute to your startup class or MauiProgram:
125+
126+
```csharp
127+
[assembly: GenerateAutoDependencies]
128+
129+
namespace DemoProject;
130+
131+
public static class MauiProgram
132+
{
133+
public static MauiApp CreateMauiApp()
134+
{
135+
var builder = MauiApp.CreateBuilder();
136+
builder.UseAutodependencies(); // Generated extension method
137+
return builder.Build();
138+
}
139+
}
140+
```
141+
142+
Generated code example:
143+
35144
```csharp
36145
using Plugin.Maui.SmartNavigation;
37146
using DemoProject;
@@ -40,7 +149,7 @@ using DemoProject.ViewModels;
40149
using DemoProject.Services;
41150
// ---------------
42151
// <auto-generated>
43-
// Generated by the MauiPageResolver Auto-registration module.
152+
// Generated by the SmartNavigation Auto-registration module.
44153
// https://github.com/matt-goldman/Plugin.Maui.SmartNavigation
45154
// </auto-generated>
46155
// ---------------
@@ -49,44 +158,106 @@ namespace DemoProject;
49158

50159
public static class PageResolverExtensions
51160
{
52-
53161
public static MauiAppBuilder UseAutodependencies(this MauiAppBuilder builder)
54162
{
55163
var ViewModelMappings = new Dictionary<Type, Type>();
56164

57165
// pages
58166
builder.Services.AddTransient<MainPage>();
59167

60-
61168
// ViewModels
62169
builder.Services.AddTransient<MainViewModel>();
63170

64-
65171
// Services
66172
builder.Services.AddSingleton<IDefaultScopedService, DefaultScopedService>();
67173
builder.Services.AddTransient<ICustomScopedService, CustomScopedService>();
68174

69-
70175
// ViewModel to Page mappings
71176
ViewModelMappings.Add(typeof(MainPage), typeof(MainViewModel));
72177

73-
74178
// Initialisation
75179
builder.Services.UsePageResolver(ViewModelMappings);
76180
return builder;
77181
}
78182
}
79183
```
80184

81-
* Lifetime attributes - override convention-based service lifetimes (singleton for services, transient for pages and ViewModels) in the source generator
185+
### Lifetime Attributes
186+
187+
Override convention-based service lifetimes (singleton for services, transient for pages and ViewModels) using attributes:
82188

83189
```csharp
84190
[Transient]
85191
public class CustomScopedService : ICustomScopedService
86192
{
87-
[...]
193+
// Will be registered as transient instead of singleton
194+
}
88195
```
89196

90-
# Getting Started
197+
## Shell vs Non-Shell Navigation
198+
199+
SmartNavigation works with both Shell and traditional navigation:
200+
201+
- **Shell Navigation**: Use `INavigationManager.GoToAsync()` with type-safe `Route` objects
202+
- **Traditional Navigation**: Use `INavigationManager.PushAsync()` and `PopAsync()`
203+
- **Automatic Detection**: `GoBackAsync()` automatically determines the navigation context
204+
205+
The `INavigationManager` intelligently handles both scenarios, so you can use the same API regardless of your navigation architecture.
206+
207+
## Migrating from PageResolver 2.x
208+
209+
### Breaking Changes
210+
211+
1. **Package Rename**: Update your NuGet package reference
212+
```xml
213+
<!-- Old -->
214+
<PackageReference Include="Plugin.Maui.PageResolver" Version="2.x" />
215+
216+
<!-- New -->
217+
<PackageReference Include="Plugin.Maui.SmartNavigation" Version="3.0" />
218+
```
219+
220+
2. **Namespace Changes**: Update all namespace imports
221+
```csharp
222+
// Old
223+
using Plugin.Maui.PageResolver;
224+
225+
// New
226+
using Plugin.Maui.SmartNavigation;
227+
```
228+
229+
3. **Source Generator Now Opt-In**: If you were using the source generator, add the attribute:
230+
```csharp
231+
[assembly: GenerateAutoDependencies]
232+
```
233+
234+
### Migration Checklist
235+
236+
- [ ] Update NuGet package from `Plugin.Maui.PageResolver` to `Plugin.Maui.SmartNavigation`
237+
- [ ] Update all `using Plugin.Maui.PageResolver` statements to `using Plugin.Maui.SmartNavigation`
238+
- [ ] If using source generator, add `[assembly: GenerateAutoDependencies]` attribute
239+
- [ ] (Optional) Migrate to `INavigationManager` for better DI support
240+
- [ ] (Optional) Implement `IViewModelLifecycle` for async ViewModel initialization
241+
- [ ] (Optional) Add `NavigatedInitBehavior` to pages that need initialization
242+
- [ ] Update any custom analyzers or code that referenced the old package name
243+
- [ ] Test all navigation flows to ensure they work correctly
244+
245+
### New Features to Consider
246+
247+
- **INavigationManager**: Consider injecting this service instead of using extension methods
248+
- **NavigatedInitBehavior**: Enables async initialization in ViewModels
249+
- **Type-Safe Routing**: Use `Route` records for Shell navigation
250+
- **Improved GoBackAsync**: Automatically handles modal, Shell, and traditional navigation
251+
252+
## Getting Started
253+
254+
Check out the [wiki](https://github.com/matt-goldman/Plugin.Maui.SmartNavigation/wiki) for detailed guides and documentation.
91255

92-
Check out the full instructions in the wiki on [using PageResolver](https://github.com/matt-goldman/Plugin.Maui.SmartNavigation/wiki/1-Using-the-PageResolver)
256+
For full examples, see the [Demo Project](src/DemoProject) which showcases:
257+
- Basic navigation with DI
258+
- Parameterized navigation
259+
- Modal navigation
260+
- Shell routing
261+
- ViewModel lifecycle behaviors
262+
- Popup support (with Mopups)
263+
- Service scope management

src/DemoProject/MainPage.xaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,16 @@
7373
Command="{Binding TriggerAggregateExceptionCommand}"
7474
FontAttributes="Bold"
7575
HorizontalOptions="Center"
76-
SemanticProperties.Hint="Throws an aggreagate exception"
77-
Text="Click to trigger aggreagate exception" />
76+
SemanticProperties.Hint="Throws an aggregate exception"
77+
Text="Click to trigger aggregate exception" />
78+
79+
<Button Command="{Binding GoToNavigationManagerDemoCommand}"
80+
BackgroundColor="{StaticResource Primary}"
81+
FontAttributes="Bold"
82+
HorizontalOptions="Center"
83+
SemanticProperties.Hint="Shows INavigationManager and IViewModelLifecycle features"
84+
Text="🆕 INavigationManager &amp; Lifecycle Demo"
85+
TextColor="White" />
7886

7987
<Image HeightRequest="310"
8088
HorizontalOptions="Center"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ContentPage x:Class="DemoProject.Pages.NavigationManagerDemoPage"
3+
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
5+
xmlns:vm="clr-namespace:DemoProject.ViewModels"
6+
xmlns:behaviors="clr-namespace:Plugin.Maui.SmartNavigation.Behaviours;assembly=Plugin.Maui.SmartNavigation"
7+
x:DataType="vm:NavigationManagerDemoViewModel"
8+
Title="NavigationManager Demo">
9+
10+
<ContentPage.Behaviors>
11+
<behaviors:NavigatedInitBehavior />
12+
</ContentPage.Behaviors>
13+
14+
<ScrollView>
15+
<VerticalStackLayout Padding="30,0"
16+
Spacing="25"
17+
VerticalOptions="Center">
18+
19+
<Label FontSize="24"
20+
HorizontalOptions="Center"
21+
SemanticProperties.HeadingLevel="Level1"
22+
Text="INavigationManager Demo" />
23+
24+
<Label FontSize="14"
25+
HorizontalOptions="Center"
26+
Text="This page demonstrates INavigationManager service and IViewModelLifecycle" />
27+
28+
<Border Padding="15"
29+
BackgroundColor="{StaticResource Primary}"
30+
StrokeShape="RoundRectangle 8">
31+
<Label FontSize="16"
32+
HorizontalOptions="Center"
33+
Text="{Binding InitMessage}"
34+
TextColor="White" />
35+
</Border>
36+
37+
<Label FontSize="14"
38+
HorizontalOptions="Center"
39+
Text="{Binding Message}" />
40+
41+
<Button Command="{Binding NavigateToScopeCheckCommand}"
42+
FontAttributes="Bold"
43+
HorizontalOptions="Center"
44+
Text="Push Page (INavigationManager)" />
45+
46+
<Button Command="{Binding NavigateToMarkupCommand}"
47+
FontAttributes="Bold"
48+
HorizontalOptions="Center"
49+
Text="Navigate to Markup Page" />
50+
51+
<Button Command="{Binding ShowModalPageCommand}"
52+
FontAttributes="Bold"
53+
HorizontalOptions="Center"
54+
Text="Show Modal Page" />
55+
56+
<Button Command="{Binding GoBackCommand}"
57+
FontAttributes="Bold"
58+
HorizontalOptions="Center"
59+
Text="Go Back (Smart Detection)" />
60+
61+
<Label FontSize="12"
62+
HorizontalOptions="Center"
63+
Margin="0,20,0,0"
64+
Text="Note: This ViewModel uses INavigationManager (injected via DI) instead of INavigation" />
65+
66+
</VerticalStackLayout>
67+
</ScrollView>
68+
69+
</ContentPage>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace DemoProject.Pages;
2+
3+
public partial class NavigationManagerDemoPage : ContentPage
4+
{
5+
public NavigationManagerDemoPage()
6+
{
7+
InitializeComponent();
8+
}
9+
}

src/DemoProject/ViewModels/MainViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,10 @@ private async Task TriggerAggregateException()
7979
throw;
8080
}
8181
}
82+
83+
[RelayCommand]
84+
private async Task GoToNavigationManagerDemo()
85+
{
86+
await Navigation.PushAsync<NavigationManagerDemoPage>();
87+
}
8288
}

0 commit comments

Comments
 (0)