0

We have a large-ish app with a ribbon. The ribbon buttons are all bound to commands in my main view model (the data context of the main app window).

The constructor for MainViewModel is starting to grow as we create lots of RelayCommands (bound to the various ribbon commands). It looks something like this:

    public MainWindowViewModel()
    {
        this.OpenProjectCommand = new RelayCommand(() =>
            {
                 // buncha code
            });

        this.ProjectTypesCommand = new RelayCommand(() =>
            {
                // more code
            });

        this.NewSectionCommand = new RelayCommand(() =>
            {
                // code code code...
            });
        // ... only three ribbon buttons down, this is gonna get huge...
    }

I'd prefer to have separate classes implementing each of the commands, rather than tons of inline code in MainViewModel's constructor. (Or creating lots of delegates in the MainViewModel, e.g. OpenProject, CanOpenProject, and then passing in references to them to the RelayCommand constructors).

Why don't I simply implement ICommand in a CommandBase and then create separate commands? Because I want to be "standard friendly" as per this question.

Is there a standard ICommand implementation I can use so that my commands are in separate classes?

I'd prefer not to add more MVVM frameworks into the mix since I'm already using MVVM Light. But I also don't want to reinvent the wheel.

Update: MainViewModel.cs doesn't need to be cluttered with scores of #regions or command methods. Extension methods aren't a good fit either IMHO.

Community
  • 1
  • 1
System.Cats.Lol
  • 1,620
  • 1
  • 26
  • 47
  • How about creating one command and inside commandparameter you specify a condition which will be used by an if statement inside the execute method of that one command? – dev hedgehog Mar 03 '14 at 21:30
  • @devhedgehog I'm looking to separate each command into its own class, but in a way that doesn't fight established MVVM conventions or the MVVM Light's tools (see linked question). – System.Cats.Lol Mar 03 '14 at 23:45

2 Answers2

1

The way I do is that I have "sub-viewmodels". For example, in the case of the MainViewModel, let's imagine that you have a PrintCommand and a CancelPrintCommand. You can have a new class called PrinterViewModel, and expose an instance of this class in the MainViewModel. Have the PrintCommand and the CancelPrintCommand in this PrinterViewModel (this also allows modular unit testing, which is neat).

Then in XAML:

Command="{Binding Main.Printer.PrintCommand}"

Alternatively, you could do

new RelayCommand(() => Printer.DoSomething())

Does that make sense?

Cheers Laurent

Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
LBugnion
  • 6,672
  • 2
  • 24
  • 28
0

You could at least create them in the getter. You can use the ?? operator.

http://msdn.microsoft.com/en-us/library/ms173224.aspx

This basically says: Return _testCommand, but create it first if it's still null.

This way, the command is not created until it's needed!

public class TestViewModel : ViewModelBase
{
    #region OpenCommand
    private RelayCommand _testCommand;
    public RelayCommand TestCommand {
        get {
            return _testCommand = _testCommand 
                ?? new RelayCommand(
                    this.ExecuteOpenCommand, 
                    this.CanOpenCommandExecute);
        }
    }
    private void ExecuteOpenCommand()
    {
        // do stuff
    }

    private bool CanOpenCommandExecute()
    {
        return true;
    }
    #endregion
}

If your goal is to organize, you can use #region and #endregion. And like we said, if your goal is to shrink the constructing process, use the ?? operator in getters. If you just hate inline code, create private methods in combination with RelayCommand, in getters.

Mtihc
  • 191
  • 1
  • 12
  • True, that might be a bit cleaner; what I really want to do though is implement each command in its own class so that MainViewModel.cs doesn't become huge. I'm wondering if there's an "accepted" way of doing this. The MVVM framework I'm using (MVVM Light) doesn't seem to support this without rolling one's own ICommand implementation. – System.Cats.Lol Mar 03 '14 at 23:42
  • That sounds perfectly fine to me. Or maybe, to avoid writing a new RelayCommand... Can't you pass a delegate method in the `base` constructor of the MVVM Light's RelayCommand? – Mtihc Mar 03 '14 at 23:51
  • I mean, write a subclass of `RelayCommand`, called `OpenProjectCommand`. And pass a `method` of your `OpenProjectCommand` class as argument in it's `base` constructor. – Mtihc Mar 03 '14 at 23:56
  • Another tip. Don't make the classes public. Only that specific ViewModel class needs to use the commands. So... keep them in the default scope instead of public. – Mtihc Mar 03 '14 at 23:59
  • I did actually try that first, sublcassing RelayCommand and passing references to its own methods to the base consructor; that didn't work because "this" didn't exist yet (as in `base(this.OpenProject, this.CanOpenProject)`) – System.Cats.Lol Mar 04 '14 at 00:04
  • Whatabout a static method? XD – Mtihc Mar 04 '14 at 00:15
  • Then how do I get my instance data? E.g., a ViewModel passed in to the command. Could you update your answer with an example of a subclassed RelayCommand? – System.Cats.Lol Mar 04 '14 at 00:53
  • It is true that it's not very practical to make a subclass of RelayCommand. I tried and it didn't work. Check my edit for another tip about extension methods :D – Mtihc Mar 04 '14 at 01:39
  • I suppose I could create extension methods for each command; pretty awkward, though! Not really what extension methods are intended for. Unless there's a standard ICommand implementation that I can use without mixing in additional MVVM frameworks on top of MVVM Light, I'll probably just roll my own. Thanks anyways! – System.Cats.Lol Mar 04 '14 at 01:50
  • You could put them all inside 1 file if you want. But I guess you're right. But, actually... check my second edit! – Mtihc Mar 04 '14 at 02:00