Joey Joystick
Joey Joystick

Reputation: 145

How to take a WPF CommandParameter to the ViewModel in MVVM?

I hope somebody can help me out here. Simplified the code for posting.

We have a main window (MvvmTestView) with a menu, and a 2nd window (SettingsView) which holds several tabs. I can open the SettingsView window alright. I can even select which Tab to open by setting this in the code.

How can I get back the correct value with the command parameter from the XAML code so that the correct tab opens?

MvvmTestView.xaml:

<Window x:Class="MvvmTest.Views.MvvmTestView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:MvvmTest.ViewModels"
    WindowStartupLocation="CenterScreen"
    Title="MvvmTestView" 
    Height="500" 
    Width="500">

    <Window.DataContext>
        <vm:MvvmTestViewModel/>
    </Window.DataContext>
    <Grid>
        <DockPanel>
            <Menu>
                <MenuItem Header="Menu">
                    <MenuItem 
                        Header="Tab01" 
                        Command="{Binding SettingsViewCommand}"
                        CommandParameter="0"/>
                    <MenuItem
                        Header="Tab02"
                        Command="{Binding SettingsViewCommand}"
                        CommandParameter="1"/>
                </MenuItem>
            </Menu>
        </DockPanel>
        <DockPanel>
            <Label Content="MainView" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </DockPanel>
    </Grid>
</Window>

SettingView.xaml

<Window x:Class="MvvmTest.Views.SettingsView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:tabData="clr-namespace:MvvmTest.Views"
    xmlns:vm="clr-namespace:MvvmTest.ViewModels"
    WindowStartupLocation="CenterScreen"
    Title="SettingsView" 
    Height="400"
    Width="400">

    <Window.DataContext>
        <vm:MvvmTestViewModel/>
    </Window.DataContext>
    <Grid>
        <TabControl
            SelectedIndex="{Binding SettingsSelectedIndex, Mode=TwoWay}">
            <tabData:Tab01View/> 
            <tabData:Tab02View/>
        </TabControl>
    </Grid>
</Window>

SettingsViewModel.cs

using System.ComponentModel;

namespace MvvmTest.ViewModels
{
    public class SettingsViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string property)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
        }

        private int _settingsSelectedIndex;
        public int SettingsSelectedIndex
        {
            get
            {
                return _settingsSelectedIndex;
            }
            set
            {
                _settingsSelectedIndex = value;
                OnPropertyChanged("SettingsSelectedIndex");
            }
        }
    }
}

MvvmTestViewModel.cs

using MvvmTest.Commands;
using MvvmTest.Views;
using System.ComponentModel;
using System.Windows.Input;

namespace MvvmTest.ViewModels
{
    internal class MvvmTestViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private SettingsViewModel SettingsViewModel;

        public MvvmTestViewModel()
        {
            SettingsViewModel = new SettingsViewModel();
            SettingsViewCommand = new SettingsViewCommand(this);
        }

        public ICommand SettingsViewCommand
        {
            get;
            private set;
        }

        public void SettingsWindow()
        {
            SetIndex();
            SettingsView settingsView = new SettingsView()
            {
                DataContext = SettingsViewModel
            };
            settingsView.ShowDialog();
        }

        public int SetIndex()
        {
            SettingsViewModel.SettingsSelectedIndex = 1;
            return SettingsViewModel.SettingsSelectedIndex;
        }
    }
}

SettingsViewCommand.cs

using MvvmTest.ViewModels;
using System;
using System.Windows.Input;

namespace MvvmTest.Commands
{
    internal class SettingsViewCommand : ICommand
    {
        private MvvmTestViewModel settingsViewModel;

        public SettingsViewCommand(MvvmTestViewModel settingsViewModel)
        {
            this.settingsViewModel = settingsViewModel;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            settingsViewModel.SettingsWindow();
        }
    }
}

Upvotes: 0

Views: 1795

Answers (1)

ASh
ASh

Reputation: 35646

I suggest to avoid creating multiple command classes like SettingsViewCommand : ICommand. Instead use some general-purpose command class (e.g. RelayCommand from MvvmFoundation NuGet package)

assuming you added MvvmFoundation to your project, refactor MvvmTestViewModel class like this:

internal class MvvmTestViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private SettingsViewModel SettingsViewModel;

    public MvvmTestViewModel()
    {
        SettingsViewModel = new SettingsViewModel();
        SettingsViewCommand = new RelayCommand<int>(SettingsWindow);
    }

    public ICommand SettingsViewCommand
    {
        get;
        private set;
    }

    public void SettingsWindow(int index)
    {
        SettingsViewModel.SettingsSelectedIndex = index;
        SettingsView settingsView = new SettingsView()
        {
            DataContext = SettingsViewModel
        };
        settingsView.ShowDialog();
    }
}

CommandParameter from a view is passed to SettingsWindow method in a viewModel and used to change selected index

Upvotes: 1

Related Questions