Two related changes that close the loop on the SettingsPage
push semantics.
1. The SettingsPage used to be registered as Transient. Each
click on the Paramètres button resolved a fresh instance,
re-bound it to the Settings singleton, and pushed it onto
the navigation stack. Repeated clicks accumulated stacked
instances, each fully bound, and the user had to tap Back
N times to leave. The fix is to register the page as a
Singleton in the DI container. There is now one and only
one SettingsPage ContentPage for the lifetime of the app:
- its DataContext is wired once, at composition time
(just after the ViewLocator is added to DataTemplates),
not on every push;
- the OpenSettingsRequested handler is a pure navigation
concern, with no DI resolution and no rebinding;
- the in-memory Settings state is preserved across visits
(any in-flight edit stays in the same instance).
2. The OpenSettingsRequested handler is guarded so that if the
SettingsPage is already at the top of NavigationStack, the
push is a no-op. NavigationPage.PushAsync does not
deduplicate; without the guard, calling it twice with the
same instance pushes it a second time, and the user has to
tap Back twice to leave. The guard is a reference comparison
on NavigationStack[Count - 1] against the singleton
instance, which is correct precisely because the page is
a singleton.
doc/architecture/postit.md is updated to match: the DI table
reflects the new lifetime, and the 'Garde anti-empilement'
section is rewritten from 'to be implemented' to the actual
implementation, including the rationale for reference
comparison and the cross-dependency between the singleton
lifetime and the guard.
The Settings-singleton invariant (in the same doc) is
unchanged: Settings is still a singleton, and adding a
transient override would still be the bug it always was.
The new SettingsPage singleton sits alongside it cleanly.
Build: 0 errors. Tests: 3/3 SettingsLoadTests green.