I'm trying to learn MVVM but am finding it a nightmare trying to understand how to correctly navigate between views in an application using MVVM. After some time researching and trying to understand different techniques I have come across an approach from Rachel Lim's blog. This technique uses a ViewModel for the application itself and keeps track of the application state such as the current page. I feel this would be a nice approach to follow for my application.
Now moving onto my problem..
What I want to achieve
I want an application that has a one main application view that will store a LoginView and a HomeView as DataTemplates and have a content control that sets the LoginView as the view displayed when the application is started. The LoginView will have a button that when pressed will open another window that has a button. When the button in the pop up window is pressed I want to change the view in the main application window from LoginView to the HomeView.
What I have so far
I have a set up the ApplicationView which works fine.
<Window x:Class="WPF_Navigation_Practice.Views.ApplicationView"
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:ignore="http://www.galasoft.ch/ignore"
xmlns:vm="clr-namespace:WPF_Navigation_Practice.ViewModels"
xmlns:views="clr-namespace:WPF_Navigation_Practice.Views"
mc:Ignorable="d ignore"
DataContext="{StaticResource ApplicationViewModel}">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:LoginViewModel}">
<views:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<views:HomeView />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentPageViewModel}" />
</Grid>
</Window>
And have set up the ApplicationViewModel as follows. Setting the current page to the LoginViewModel.
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using WPF_Navigation_Practice.Interfaces;
namespace WPF_Navigation_Practice.ViewModels
{
/// <summary>
/// This class contains properties that a View can data bind to.
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class ApplicationViewModel : ViewModelBase
{
#region Fields
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
#endregion
public ApplicationViewModel()
{
// Add available pages
PageViewModels.Add(new LoginViewModel());
PageViewModels.Add(new HomeViewModel());
PageViewModels.Add(new CodeViewModel());
// Set starting page
CurrentPageViewModel = PageViewModels[0];
}
#region Properties / Commands
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
{
_changePageCommand = new RelayCommand<object>(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return _changePageCommand;
}
}
public List<IPageViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
_pageViewModels = new List<IPageViewModel>();
return _pageViewModels;
}
}
public IPageViewModel CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
RaisePropertyChanged("CurrentPageViewModel");
}
}
}
#endregion
#region Methods
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
PageViewModels.Add(viewModel);
CurrentPageViewModel = PageViewModels
.FirstOrDefault(vm => vm == viewModel);
}
#endregion
}
}
When I run the application it will display my main Application window which displays the loginView which is a UserControl and is set as the currentPageViewModel with ContentPresenter.
When the button in the LoginView UserControl is clicked it will open another window. As per the image below.
Here is the XAML for that window.
<Window x:Class="WPF_Navigation_Practice.Views.CodeView"
x:Name="CodeWindow"
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:ignore="http://www.galasoft.ch/ignore"
xmlns:z="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:viewModels="clr-namespace:WPF_Navigation_Practice.ViewModels"
mc:Ignorable="d ignore"
d:DesignWidth="623.224" d:DesignHeight="381.269"
DataContext="{Binding CodeViewModel, Source={StaticResource ApplicationViewModel}}">
<Grid>
<Button Content="Ok"
HorizontalAlignment="Left"
Margin="235,166,0,0"
VerticalAlignment="Top"
Width="138"
FontSize="20"
Height="67"/>
<Label Content="Second Window" HorizontalAlignment="Left" Margin="166,56,0,0" VerticalAlignment="Top" FontSize="36"/>
</Grid>
My Problem
What I want to achieve is when the 'Ok' button in the secondView window is clicked, I want to change the currentPageViewModel in the ApplicationView Window from the LoginView to display the HomeView but am confused on how I would go about achieving this. Any help would be greatly appreciated.