0

I am implementing a WPF application and I am switching view models on button click. I had to implement an navigation store by youtube tutorial. When I click a button, navigateCommand will execute, creating a new viewModel and notifying view to change. However I dont understand what is method OnCurrentViewModelChanged() doing and why is it needed, action CurrentViewModelChanged is returning void, and is empty? Or am I missing something? What is CurrentViewModelChanged doing? Can someone please explain?

public class NavigationStore
    {
        public event Action CurrentViewModelChanged;

        private NotifyPropertyChanged currentViewModel;
        public NotifyPropertyChanged CurrentViewModel
        {
            get => currentViewModel;
            set
            {
                currentViewModel = value;
                OnCurrentViewModelChanged();
            }
        }

        private void OnCurrentViewModelChanged()
        {
            CurrentViewModelChanged?.Invoke();
        }
    }
public class NavigateCommand<TViewModel> : CommandBase where TViewModel : NotifyPropertyChanged
    {
        private readonly NavigationStore _navigationStore;
        private readonly Func<TViewModel> _createViewModel;

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

        public override void Execute()
        {
            _navigationStore.CurrentViewModel = _createViewModel();
        }
    }
public class MainViewModel : NotifyPropertyChanged
    {
        private readonly NavigationStore _navigationStore;

        public NotifyPropertyChanged CurrentViewModel => _navigationStore.CurrentViewModel;

        public MainViewModel(NavigationStore navigationStore)
        {
            _navigationStore = navigationStore;

            _navigationStore.CurrentViewModelChanged += OnCurrentViewModelChanged;
        }

        private void OnCurrentViewModelChanged()
        {
            OnPropertyChanged(nameof(CurrentViewModel));
        }
    }
juraj14466
  • 53
  • 4
  • 1
    `OnCurrentViewModelChanged` is raising an event. So that anyone who uses your `NavigationStore` class can subscribe to the event and be notified when the current view model changes -- which happens when you navigate. – Joe Apr 13 '22 at 17:12
  • 1
    From your posted code I can tell that the tutorial is awful. `OnCurrentViewModelChanged()` is the event invocator of the `CurrentViewModelChanged` event. See [How to publish events that conform to .NET Guidelines (C# Programming Guide)](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-publish-events-that-conform-to-net-framework-guidelines) and [Events (C# Programming Guide)](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/) to learn about events and how they are properly defined (and used). – BionicCode Apr 13 '22 at 18:13
  • 1
    Never use `Action` as event delegate. Use `EventHandler` instead (or `EventHandler` if you need to send data with the event). I highly recommend to stop following your current tutorial unless you want to learn some code smells. There are better tutorials out there. See this example for basic page navigation: [C# WPF Navigation Between Pages (Views)](https://stackoverflow.com/a/61323201/3141792). – BionicCode Apr 13 '22 at 18:13

1 Answers1

2

So first of all, I also followed his tutorials (it's most likely SingletonSean's) and I don't share @BenicCode's opinion on that (tho I'm not a professional at WPF like he may be), I really like his explanations and solutions to problems. Besides, he keeps changing the project throughout the guide, implementing better solutions and explaining why it's better to use this than that.

The OnCurrentViewModelChanged() method raises an event so that any method that is subscribed to it will be invoked. However, you actually don't need it, you can implement NavigationStore like this:

NavigationStore.cs

public class NavigationStore : INavigationStore
    {
        private ViewModelBase? _currentViewModel;
        public ViewModelBase? CurrentViewModel
        {
            get => _currentViewModel;
            set
            {
                _currentViewModel?.Dispose();

                _currentViewModel = value;
                NavigationStateChanged?.Invoke();
            }
        }

        public event Action? NavigationStateChanged;
    }

And now, in your MainViewModel, you can simply subscribe the NavigationStateChanged action to OnCurrentViewModelChanged() instead of having one more method in your navigation store.

MainViewModel.cs

public class MainViewModel : ViewModelBase
    {
        private readonly INavigationStore _navigationStore;

        public ViewModelBase? CurrentViewModel => _navigationStore.CurrentViewModel;

        public MainViewModel(INavigationStore navigationStore)
        {
            _navigationStore = navigationStore;

            _navigationStore.NavigationStateChanged += OnNavigator_NavigationStateChanged;
        }

        private void OnNavigator_NavigationStateChanged()
        {
            OnPropertyChanged(nameof(CurrentViewModel));
        }
}

It's basically the same, but a bit simpler (correct me if I'm wrong). By subscribing NavigationStateChanged to OnNavigator_NavigationStateChanged, whenever NavigationStateChanged is raised, OnNavigator_NavigationStateChanged will fire too, which will notify your UI to change (since you bind the ContentControl's Content property to the CurrentViewModel property).

MainWindow.xaml

<Grid>
   <ContentControl Content="{Binding CurrentViewModel}" />
</Grid>

At this point of the tutorial he just wanted to demonstrate really basic navigation. As you progress further, things get cleaner and more complicated. I really suggest finishing his tutorials, there might be better guides, but as a starting point, I couldn't find any better channel.

drazse
  • 93
  • 1
  • 7