Click here to Skip to main content
15,910,603 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
I am new to WPF and working on a WPF app. I have build navigation system using looking at some sample. I have buttons on main window which bind to NavigationViewModel

<Button Content="Items List" Command="{Binding ItemsListCmd}"/>
<Button Content="Items" Command="{Binding ItemsEditCmd}"/>


In MainWinodw.xaml.cs
public MainWindow()
{
    InitializeComponent();
    this.DataContext = new NavigationViewModel();
}

In MainWindow.xaml I have bindings as below

<ContentControl x:Name="Pages" DockPanel.Dock="Right" Content="{Binding SelectedViewModel}"/>

Here is the NavigationViewModel class having ItemsListCmd and ItemsEditCmd commands.

namespace CC
{
	class NavigationViewModel : INotifyPropertyChanged
	{
		public event PropertyChangedEventHandler PropertyChanged;

		public ICommand ItemsListCmd { get; set; }
		public ICommand ItemsEditCmd { get; set; }

		private object selectedViewModel;

		public object SelectedViewModel
		{
			get {
				return selectedViewModel;
			}
			set {
				selectedViewModel = value;
				OnPropertyChanged("SelectedViewModel");
			}
		}

		public NavigationViewModel()
		{
			ItemsListCmd = new BaseCommand(OpenItemsList);
			ItemsEditCmd = new BaseCommand(OpenItemsEdit);
		}

		private void OpenItemsList(object obj)
		{
			SelectedViewModel = new ItemsListView();
		}

		private void OpenItemsEdit(object obj)
		{
			SelectedViewModel = new ItemsEditViewModel();
		}

		private void OnPropertyChanged(string propName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propName));
			}
		}
	}
}

I want to know how can I execute ItemsListCmd or ItemsEditCmd commands from code behind on some custom event/action, like when user clicks on cancel button or in any other View/ViewModel?

What I have tried:

I tried to calling "NavigationViewModel" class but as that is used as Binding so not getting direct access to that class.
I also tried to google for the solution but couldn't find anything.
Posted
Updated 2-Aug-17 9:36am
v2
Comments
Member 12814959 3-Aug-17 6:42am    
@Graeme_Grant, thanks for your reply but I would prefer a solution without using my third party frameworks. As at this stage, I want some speedy solution without need to learn another framework on top of WPF.
Graeme_Grant 4-Aug-17 8:07am    
MVVMLight is not a framework, it is a library. If you don't want to add their DLL to your project, their source code for the library is open source and you can add to your own app.
Graeme_Grant 4-Aug-17 8:10am    
Here is a CodeProject article that explains how to implement a Command for use with buttons: Basic MVVM and ICommand Usage Example[^] and here is an article on implementing the Mediator [Messaging] pattern: MVVM Mediator Pattern[^]

I still recommend MVVMLight library as the better solution. ;)

1 solution

Have a look at MVVM Light Toolkit[^] - This library simplifies a number of MVVM concepts including commands.

For Example, I am using a Frame control to host my two test navigation views:
XML
<Window 
    x:Class="WpfApp2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    mc:Ignorable="d"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    xmlns:local="clr-namespace:WpfApp2"

    Title="WPF Navigation Example" Height="350" Width="525">

    <Window.Resources>
        <Style x:Key="FrameStyle1" TargetType="{x:Type Frame}">
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="NavigationUIVisibility" Value="Hidden" />
            <Setter Property="Margin" Value="0" />
            <Setter Property="SnapsToDevicePixels" Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Frame}">
                        <ContentPresenter x:Name="PART_FrameCP"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <DockPanel>
        <Border DockPanel.Dock="top" Background="AliceBlue" Padding="10">
            <StackPanel Orientation="Horizontal" >
                <Button Content="Page1" Padding="10 5" Margin="0 0 10 0"
                        Command="{Binding MenuCommand}" CommandParameter="Page1"/>
                <Button Content="Page2" Padding="10 5" Margin="0 0 10 0"
                        Command="{Binding MenuCommand}" CommandParameter="Page2"/>
            </StackPanel>
        </Border>
        <Frame Source="{Binding NavigateTo}" Style="{StaticResource FrameStyle1}" />
    </DockPanel>

</Window>

ViewModel for the MainWindow
C#
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;

namespace WpfApp2
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            MenuCommand = new RelayCommand<string>(msg => NavigateTo = $"{msg}.xaml");
        }

        public RelayCommand<string> MenuCommand { get; }

        private string navigateTo = "Page1.xaml";
        public string NavigateTo
        {
            get => navigateTo;
            set => Set(ref navigateTo, value);
        }
    }
}

And the two Pages (Navigation Views)
XML
<Page x:Class="WpfApp2.Page1"
      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:WpfApp2"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Page1">

    <Grid>
        <Viewbox>
            <TextBlock Text="Page 1"/>
        </Viewbox>
    </Grid>
</Page>

<Page x:Class="WpfApp2.Page2"
      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:WpfApp2"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Page2">

    <Grid>
        <Viewbox>
            <TextBlock Text="Page 2"/>
        </Viewbox>
    </Grid>
</Page>

Now I can click on the buttons and the view will change.

Update

Having another read of your question I realise that you may only want the navigation buttons on the child views. So here is another solution using MVVMLight Messaging. The Messaging system enables simple communication between the views.

MainWindow:
XML
<Window
    x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    mc:Ignorable="d"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    xmlns:local="clr-namespace:WpfApp1"

    Title="WPF Navigation Example 2" Height="350" Width="525">

    <Window.Resources>
        <Style x:Key="FrameStyle1" TargetType="{x:Type Frame}">
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="NavigationUIVisibility" Value="Hidden" />
            <Setter Property="Margin" Value="0" />
            <Setter Property="SnapsToDevicePixels" Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Frame}">
                        <ContentPresenter x:Name="PART_FrameCP"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <DockPanel>
        <Frame Source="{Binding NavigateTo}" Style="{StaticResource FrameStyle1}" />
    </DockPanel>

</Window>

ViewModel for the MainWindow
C#
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;

namespace WpfApp1
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            Messenger.Default.Register<MenuNavigationMessage>
                (
                     this,
                     (navMsg) => NavigateTo = $"{navMsg.Message}.xaml"
                );
        }

        private string navigateTo = "Page1.xaml";
        public string NavigateTo
        {
            get => navigateTo;
            set => Set(ref navigateTo, value);
        }
    }
}

The Messaging class used to pass typed messages between the viewmodels
C#
namespace WpfApp1
{
    public class MenuNavigationMessage
    {
        public MenuNavigationMessage(string msg)
        {
            Message = msg;
        }
        public string Message { get; }
    }
}

A base view model for the navigation views with common functionality - messaging and buttons:
C#
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

namespace WpfApp1
{
    public abstract class NavigationViewModelBase : ViewModelBase
    {
        public NavigationViewModelBase()
        {
            MenuCommand = new RelayCommand<string>(msg => Messenger.Default.Send(new MenuNavigationMessage(msg)));
        }

        public RelayCommand<string> MenuCommand { get; }
    }
}

And the two Pages (Navigation Views)
XML
<Page x:Class="WpfApp1.Page1"
      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:WpfApp1"
      mc:Ignorable="d"  d:DesignHeight="300" d:DesignWidth="300"
      Title="Page1">

    <Page.DataContext>
        <local:Page1ViewModel/>
    </Page.DataContext>

    <Grid>
        <Button Content="Go to Page2" Padding="10 5" Margin="10"
                HorizontalAlignment="left" VerticalAlignment="Top"
                        Command="{Binding MenuCommand}" CommandParameter="Page2"/>
        <Viewbox>
            <TextBlock Text="Page 1"/>
        </Viewbox>
    </Grid>
</Page>

<Page x:Class="WpfApp1.Page2"
      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:WpfApp1"
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"
      Title="Page2">

    <Page.DataContext>
        <local:Page2ViewModel/>
    </Page.DataContext>

    <Grid>
        <Button Content="Go to Page1" Padding="10 5" Margin="10"
                HorizontalAlignment="left" VerticalAlignment="Top"
                        Command="{Binding MenuCommand}" CommandParameter="Page1"/>
        <Viewbox>
            <TextBlock Text="Page 2"/>
        </Viewbox>
    </Grid>
</Page>

And the viewmodels for the pages
C#
namespace WpfApp1
{
    public class Page1ViewModel : NavigationViewModelBase
    {
    }

    public class Page2ViewModel : NavigationViewModelBase
    {
    }
}

Now the navigation buttons are on each page. Clicking on them will switch the view.
 
Share this answer
 
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900