What is the MVVM way to call WPF command from another viewmodel?

Viewed 7888

I'm trying to learn more about MVVM implementation in WPF and currently need some guidance on navigation using ViewModels. I'm following WPF navigation example from Rachel's blog and need a way to call Command of ApplicationViewModel from other ViewModel.

As per the blog, switching views from MainWindow is pretty clear, but I want to know more about inter-view navigation i.e. say I've Home, Product and Contact button on MainWindow along with View and ViewModel classes, now I want to open Contact page from some button inside Home view instead of MainWindow. I have written some code in Home ViewModel to achieve the same but I doubt whether this is the best practice of MVVM. And is there any way to achieve the same from HomeView.XAML?

Code Snippet from blog - ApplicationViewModel.cs

private ICommand _changePageCommand;

private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;

public ApplicationViewModel()
{
    // Add available pages in c'tor
    PageViewModels.Add(new HomeViewModel(this));
    PageViewModels.Add(new ProductsViewModel());
    PageViewModels.Add(new ContactViewModel());
}

public ICommand ChangePageCommand
{
    get
    {
        if (_changePageCommand == null)
            _changePageCommand = new RelayCommand(
              p => ChangeViewModel((IPageViewModel)p), p => p is IPageViewModel);

        return _changePageCommand;
    }
}

private void ChangeViewModel(IPageViewModel viewModel)
{
    if (!PageViewModels.Contains(viewModel))
        PageViewModels.Add(viewModel);

    CurrentPageViewModel = PageViewModels.FirstOrDefault(vm => vm == viewModel);
}

Code Snippet from blog - ApplicationView.xaml

<Window.Resources>
    <DataTemplate DataType="{x:Type local:HomeViewModel}">
        <local:HomeView />
    </DataTemplate>

    <!-- Data template for other views -->
</Window.Resources>

<DockPanel>
    <Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
        <ItemsControl ItemsSource="{Binding PageViewModels}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Content="{Binding Name}" 
                            Command="{Binding DataContext.ChangePageCommand, 
                            RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                            CommandParameter="{Binding }"/>

 <!--All closing tags-->

My code inside HomeViewModel.cs

// This is the command to get bind with my button inside Home view to invoke Contact view

private ICommand _loadContactCommand;
public ICommand LoadContactCommand
{
    get
    {
        if (_loadContactCommand == null)
            _loadContactCommand = new RelayCommand(p => LoadOtherView());

        return _loadContactCommand;
    }
}

private void LoadOtherView()
{
    // _appVM is the instance of 'ApplicationViewModel' which is being set from c'tor
    // Even I'm thinking to pass Contact view member of ApplicationViewModel class here, 
    // as I need exactly the same instance of the Contact which has been created earlier

    _appVM.ChangePageCommand.Execute(new ContactViewModel());
}
1 Answers
Related