Krzysztof Królikowski Krzysztof Królikowski - 1 month ago 20
C# Question

Navigation from one view to another in WPF MVVM

I wrote code which should navigate between user controls in WPF application using MVVM, but I realised that this code doesn't work.
From window

LoginView
I want to change the view to
VotingCardView
.

Actually, after clicking on the button in the
LoginView
, the method
DisplayVCV
gets executed, but the view is not going to change. What am I doing wrong?

MainView.xaml:

<Window x:Class="ElectionCalculator.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ElectionCalculator"
xmlns:v="clr-namespace:ElectionCalculator.View"
xmlns:vm="clr-namespace:ElectionCalculator.ViewModel"
mc:Ignorable="d"
Title="Election calculator" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<ContentControl Content="{Binding ViewModel}" />
</Window>


LoginView.xaml:

<UserControl x:Class="ElectionCalculator.View.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ElectionCalculator.View"
xmlns:vm="clr-namespace:ElectionCalculator.ViewModel"

mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">

<Grid>
<Button Command="{Binding DataContext.DisplayVC, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=OneWay}" Margin="161,147,47,124" />
</Grid>

</UserControl>


MainViewModel.cs

class MainViewModel : BaseViewModel
{
public BaseViewModel ViewModel { get; set; }

public MainViewModel()
{
ViewModel = new LoginViewModel();
}

public ICommand DisplayVC { get { return new RelayCommand(DisplayVCV); } }

public void DisplayVCV()
{
ViewModel = new VotingCardViewModel();

MessageBox.Show("DisplayVCCommandExecuted");
}
}

Answer

Your ViewModel property implementation doesn't raise a PropertyChanged event when the value changes. This is usually done via an INotifyPropertyChanged implementation. Because of that, your view doesn't get notified that something has changed.

In your case, this means that you need a backing field for your ViewModel property and implement your ViewModel property similar to this:

private BaseViewModel _viewModel;
public BaseViewModel ViewModel
{
  get { return _viewModel; }
  set
  {
    if(_viewModel != value) 
    {
      _viewModel = value;
      OnPropertyChanged("ViewModel");
    }
  }
}

Since you are already deriving from BaseViewModel I assume that the method OnPropertyChanged (or some method with a similar name) is implemented there. It is also quite common that you don't have to specify the property name ("ViewModel") as an argument, since lots of implementations use the [CallerMemberName] attribute for this purpose.