views:

882

answers:

2

I've got the following MainView.xaml file that works well as a MVVM menu switcher. I've got these pairs:

  • Page1View / Page1ViewModel
  • Page2View / Page2ViewModel

in my MainViewModel I fill an ObservableCollection with both ViewModels, then when the user clicks the Next button, it calls NextPageCommand in MainViewModel which switches out CurrentPageViewModel with a new ViewModel which is then displayed with an appropriate View, works nicely.

I also have a Menu being filled with all the titles from the ViewModels in the Observable collection, which also works nicely.

However, each MenuItem has a Command="{Binding SwitchPageCommand}" which SHOULD call SwitchPageCommand on the MainViewModel and not on e.g. Page1ViewModel or Page2ViewModel.

So how can I indicate in the template not to bind to the current ViewModel but the ViewModel which contains that ViewModel, e.g. something like this:

PSEUDO-CODE:

<DataTemplate x:Key="CodeGenerationMenuTemplate">
    <MenuItem 
     Command="{Binding <parentViewModel>.SwitchPageCommand}" 
     Header="{Binding Title}" 
     CommandParameter="{Binding Title}"/>
</DataTemplate>

Here is MainViewModel:

<Window x:Class="TestMenu234.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:c="clr-namespace:TestMenu234.Commands"
    xmlns:vm="clr-namespace:TestMenu234.ViewModels"
    xmlns:v="clr-namespace:TestMenu234.Views"
    Title="Main Window" Height="400" Width="800">

    <Window.Resources>
        <DataTemplate x:Key="CodeGenerationMenuTemplate">
            <MenuItem Header="{Binding Title}" Command="{Binding SwitchPageCommand}" CommandParameter="{Binding Title}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:Page1ViewModel}">
            <v:Page1View/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:Page2ViewModel}">
            <v:Page2View/>
        </DataTemplate>
    </Window.Resources>

    <DockPanel>

        <Menu DockPanel.Dock="Top">
            <MenuItem Header="Code _Generation" ItemsSource="{Binding AllPageViewModels}"
                      ItemTemplate="{StaticResource CodeGenerationMenuTemplate}"/>
        </Menu>

        <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
            <Button Margin="5" Content="Next Page" Command="{Binding NextPageCommand}"/>
        </StackPanel>

        <ContentControl
            Content="{Binding CurrentPageViewModel}"/>

    </DockPanel>
</Window>
A: 

There is no generic way to access the parent ViewModel, it depends on your implementation... You probably need to add a property to your PageViewModels that refers to the MainView. You can initialize this property in the constructor. Assuming both Page1ViewModel and Page2ViewModel inherit from a PageViewModel base class :

...

public PageViewModel(MainViewModel main)
{
    this._main = main;
}

public MainViewModel Main
{
    get { return _main; }
}

...

EDIT: I forgot the last part...

In your DataTemplate, refer to the command like that :

<DataTemplate x:Key="CodeGenerationMenuTemplate">
    <MenuItem 
        Command="{Binding Main.SwitchPageCommand}" 
        Header="{Binding Title}" 
        CommandParameter="{Binding Title}"/>
</DataTemplate>
Thomas Levesque
A: 

The answer is this:

<DataTemplate x:Key="CodeGenerationMenuTemplate">
    <MenuItem 
        Header="{Binding Title}" 
        Command="{Binding DataContext.SwitchPageCommand,
    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}" 
        CommandParameter="{Binding Title}"/>
</DataTemplate>

I just saw that Nir had given me the syntax to solve the above issue on this question: What is the best way in MVVM to build a menu that displays various pages?.

Edward Tanguay