Search code examples
c#xamlmvvmviewmodelmvvm-light

Separate MVVM command to each class


I am a newbie in the MVVM pattern. After a long time googling, I start with a MVVM command for a button. For better, I am using MVVM Light NuGet package to write code. There are 2 buttons, on click on each button I do something (here, a click on button A shows 'Implement feature A'). This below code works fine:

namespace ButtonMVVM
{
    class CommandViewModel : ViewModelBase
    {
        public RelayCommand FeatureA { get; private set; }
        public RelayCommand FeatureB { get; private set; }

        public CommandViewModel()
        {
            this.FeatureA = new RelayCommand(this.FeatureAFunc);
            this.FeatureB = new RelayCommand(this.FeatureBFunc);
        }

        private void FeatureAFunc()
        {
            MessageBox.Show("Implement feature A");
        }

        private void FeatureBFunc()
        {
            MessageBox.Show("Implement feature B");
        }
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

And in xaml file:

<Window x:Class="ButtonMVVM.MainWindow"
        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:local="clr-namespace:ButtonMVVM"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

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

    <Grid>
        <Button Command='{Binding FeatureA}' x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="54,81,0,0" VerticalAlignment="Top" Width="75"/>
        <Button Command='{Binding FeatureB}' x:Name="button1" Content="Button" HorizontalAlignment="Left" Margin="179,81,0,0" VerticalAlignment="Top" Width="75"/>

    </Grid>
</Window>

But now, imagine I have a lot of features (i.e. 10 features), and there is a lot of code, a lot of helper methods for each feature. I can't write all of them in 1 class CommandViewModel.

Is there any way to place each feature into a designated class (i.e featureA.cs, featureB.cs...)?


Solution

  • If you want to have just multiple files, you can make your CommandViewModel class partial and create multiple files for that single class. Then, place each feature into the corresponding file:

    CommandViewModel.cs:

    partial class CommandViewModel : ViewModelBase
    {
        public CommandViewModel()
        {
            this.FeatureA = new RelayCommand(this.FeatureAFunc);
            this.FeatureB = new RelayCommand(this.FeatureBFunc);
        }
    }
    

    CommandViewModel.FeatureA.cs:

    partial class CommandViewModel
    {
        public RelayCommand FeatureA { get; }
    
        private void FeatureAFunc()
        {
            MessageBox.Show("Implement feature A");
        }
    }
    

    CommandViewModel.FeatureB.cs:

    partial class CommandViewModel
    {
        public RelayCommand FeatureB { get; }
    
        private void FeatureBFunc()
        {
            MessageBox.Show("Implement feature B");
        }
    }
    

    Technically, this will still be a single class, but partitioned into multiple files. Another disadvantage is that you will only have a single constructor, so you will have to place the initialization logic for all features in a single file. The advantage of this is that you don't need to touch your XAML.

    An alternative variant: use multiple viewmodels, for each feature a designated one.

    MainViewModel.cs:

    class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            this.FeatureA = new ViewModelFeatureA();
            this.FeatureB = new ViewModelFeatureB();
        }
    
        public ViewModelFeatureA FeatureA { get; }
        public ViewModelFeatureB FeatureB { get; }
    }
    

    ViewModelFeatureA.cs:

    class ViewModelFeatureA : ViewModelBase
    {
        public ViewModelFeatureA ()
        {
            this.FeatureACommand = new RelayCommand(this.FeatureAFunc);
        }
    
        public RelayCommand FeatureACommand { get; }
    
        private void FeatureAFunc()
        {
            MessageBox.Show("Implement feature A");
        }
    }
    

    This will let you to encapsulate your logic in different classes and files. However, you will need to change the bindings of your view.

    Either access the viewmodels via properties of the main viewmodel:

    <Button Command='{Binding FeatureA.FeatureACommand}'/>
    

    Or change the data context of the related view parts:

    <Grid DataContext="{Binding FeatureA}>
        <Button Command='{Binding FeatureACommand}'/>
    </Grid>