C
C#2y ago
Indeed

❔ WPF NavigationService Implement NavigateBack

Hi! I am stuck on a problem of navigating back my navigation singleton
public class NavigationStore {
private ViewModelBase? _currentViewModel;
public ViewModelBase? CurrentViewModel {
get => _currentViewModel;
set {
_currentViewModel?.Dispose();
_currentViewModel = value;
OnCurrentViewModelChanged();
}
}

public event Action CurrentViewModelChanged;

private void OnCurrentViewModelChanged() {
CurrentViewModelChanged?.Invoke();
}
}
public class NavigationStore {
private ViewModelBase? _currentViewModel;
public ViewModelBase? CurrentViewModel {
get => _currentViewModel;
set {
_currentViewModel?.Dispose();
_currentViewModel = value;
OnCurrentViewModelChanged();
}
}

public event Action CurrentViewModelChanged;

private void OnCurrentViewModelChanged() {
CurrentViewModelChanged?.Invoke();
}
}
navigation service
namespace WPFWeather.Services.NavigationService;
public class NavigationService<TViewModel> where TViewModel : ViewModelBase {
private readonly NavigationStore _navigationStore;
private readonly Func<TViewModel> _createViewModel;

public NavigationService(NavigationStore navigationStore, Func<TViewModel> createViewModel) {
_navigationStore = navigationStore;
_createViewModel = createViewModel;
}

public void Navigate() {
_navigationStore.CurrentViewModel = _createViewModel();
}
}
namespace WPFWeather.Services.NavigationService;
public class NavigationService<TViewModel> where TViewModel : ViewModelBase {
private readonly NavigationStore _navigationStore;
private readonly Func<TViewModel> _createViewModel;

public NavigationService(NavigationStore navigationStore, Func<TViewModel> createViewModel) {
_navigationStore = navigationStore;
_createViewModel = createViewModel;
}

public void Navigate() {
_navigationStore.CurrentViewModel = _createViewModel();
}
}
that i register to DI
services.AddTransient<WeatherHomeViewModel>();
services.AddSingleton<Func<WeatherHomeViewModel>>((s) => () => s.GetRequiredService<WeatherHomeViewModel>());
services.AddSingleton<NavigationService<WeatherHomeViewModel>>();

services.AddTransient<WelcomeViewModel>();
services.AddSingleton<Func<WelcomeViewModel>>((s) => () => s.GetRequiredService<WelcomeViewModel>());
services.AddSingleton<NavigationService<WelcomeViewModel>>();
services.AddTransient<WeatherHomeViewModel>();
services.AddSingleton<Func<WeatherHomeViewModel>>((s) => () => s.GetRequiredService<WeatherHomeViewModel>());
services.AddSingleton<NavigationService<WeatherHomeViewModel>>();

services.AddTransient<WelcomeViewModel>();
services.AddSingleton<Func<WelcomeViewModel>>((s) => () => s.GetRequiredService<WelcomeViewModel>());
services.AddSingleton<NavigationService<WelcomeViewModel>>();
and then inject
public SetLocationViewModel(IWeatherProvider weatherProvider, AppStore appStore, NavigationService<WeatherHomeViewModel> weatherHomeNavigationService)
public SetLocationViewModel(IWeatherProvider weatherProvider, AppStore appStore, NavigationService<WeatherHomeViewModel> weatherHomeNavigationService)
However using this architecture I cannot implement going back
8 Replies
Indeed
Indeed2y ago
To be clear, I've tried by both having a property on a NavigationStore called _previousViewModel that was set in viewModel setter
private ViewModelBase? _currentViewModel;
public ViewModelBase? CurrentViewModel {
get => _currentViewModel;
set {
_currentViewModel?.Dispose();
PreviousViewModel = _currentViewModel;
_currentViewModel = value;
OnCurrentViewModelChanged();
}
}
private ViewModelBase? _currentViewModel;
public ViewModelBase? CurrentViewModel {
get => _currentViewModel;
set {
_currentViewModel?.Dispose();
PreviousViewModel = _currentViewModel;
_currentViewModel = value;
OnCurrentViewModelChanged();
}
}
However then I have to directly interact with NavigationStore to set currentModel to previousModel, because to use NavigationService I need a generic plus I am using .Dispose() which means I need to recreate the model but to recreate the model i need to know it's type that seems like ideal solution but doesnt really seem achievable public NavigationBackService(NavigationStore navigationStore, Func<typeof(NavigationStore.PreviousViewModel)> createViewModel) {
Indeed
Indeed2y ago
SingletonSean
YouTube
Models - WPF MVVM TUTORIAL #1
Learn about the model layer of MVVM, which contains the core domain logic of the application. I also go through the setup in Visual Studio and introduce the domain for the application. In this series, I demonstrate all the key components of a WPF MVVM application. This series will lay the foundations for building your own application. TIMESTAM...
canton7
canton72y ago
Having a NavigationService<HomeViewModel> looks really weird. Why the generics? Why not keep the non-generic NavigationService, and inject a Func<HomeViewModel> into it (and inject Func<OtherViewModel> factories for the other VM types? That means the navigation service knows the types of VMs it's navigating to, but... so what? If anything should, it should. Then anyone can get an instance of the NavigationService and call GoHome or GoToLogin or Back or whatever
canton7
canton72y ago
GitHub
Stylet/NavigationController.cs at master · canton7/Stylet
A very lightweight but powerful ViewModel-First MVVM framework for WPF for .NET Framework and .NET Core, inspired by Caliburn.Micro. - Stylet/NavigationController.cs at master · canton7/Stylet
canton7
canton72y ago
You can then implement a Back function by just keeping a stack of VMs, or a stack of Func<ViewModelBase> instances
Indeed
Indeed2y ago
Thank you very much! This worked! ❤️ Ofc its in the barebone stages rn, however your solution did shine a lot of light onto my problem As for the generics, I'm used to them so I don't really mind and that's what has been used in the video. It also simplifies using them with commands e.g.
public class NavigateCommand<TViewModel> : CommandBase where TViewModel : ViewModelBase {
private readonly NavigationService<TViewModel> _navigationService;

public NavigateCommand(NavigationService<TViewModel> navigationService) {
_navigationService = navigationService;
}

public override void Execute(object parameter) {
_navigationService.Navigate();
}
}
public class NavigateCommand<TViewModel> : CommandBase where TViewModel : ViewModelBase {
private readonly NavigationService<TViewModel> _navigationService;

public NavigateCommand(NavigationService<TViewModel> navigationService) {
_navigationService = navigationService;
}

public override void Execute(object parameter) {
_navigationService.Navigate();
}
}
canton7
canton72y ago
It does mean that if you're got a menu page which can navigate to lots of places, the menu page is the thing that takes the hit, needing lots of NavigationService<T> types injected into it. The other thing is that you can't pass parameters to the page you're navigating to Tbh, I normally end up building a big state machine inside the navigation service. So each page has a definted set of pages it can transition to, and the NavigateToXXX method calls turn into events on the state machine.
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.