3
votes

I'm trying to learn how to use WPF commands, and how they fit into the MVVM pattern. I understand that some controls, like a button or menu, have a Command property which when set to an instance of an ICommand class connects the button to that command. Once this is set the button will then disable and enable with the CanExecuteChanged event of the command, and clicking the control will call the Execute method of the command.

However, where should the instance of each ICommand live? I've seen various different options in tutorials and I'm not sure which is correct. In some examples a static "ApplicationCommands" class is created and an instance of each command is assigned to a static property of that class. In other examples I've seen commands set as properties of the ViewModel, and in others of the View/Window itself. What's the preferred place for the command instances to live?

Also, how does a command relate back to the View, View Model, or Model? Which of these components should the command be aware of and or manipulate? What should happen when a command is executed? Should it call some method of the Model which then communicates changes back to the View Model/View? Or should a command communicate to the model through a method of the View Model?

2

2 Answers

1
votes

In some examples a static "ApplicationCommands" class is created and an instance of each command is assigned to a static property of that class

You do this for commands that you want to be able to access from anywhere. For example, if you wanted a keybinding of F1 to always bring up a help screen, then you would have this command implemented in one globally accessible spot and then bind to it from the various screens.

seen commands set as properties of the ViewModel, and in others of the View/Window itself.

If the command is doing something with the data, then the ViewModel is a good place for it. If the command doesn't need to do anything with the data then put it in the code behind of the View (as it has nothing to do with the ViewModel and you don't need to pollute the ViewModel with it). An interesting case is when you are doing something like popping up a dialog in response to a keystroke, and you need to pass the currently selected grid item through to the dialog - where should the command go? In this case I would put it in the code behind of the view as there is no compelling reason to put it in the ViewModel - the selected item can be retrieved from the ViewModel by the View if necessary.

Many times I have seen commands needlessly put into the ViewModel simply because people thought that was the only way to do it. The rule of thumb I use is: if it is doing UI related work then it belongs in the code behind of the view. If it is doing data related work then it can go in the ViewModel. If it is doing a mixture then consider breaking up the functionality across both the View and ViewModel.

Which of these components should the command be aware of and or manipulate?

Only the ones it needs to know about. Access to components like the Model from the ViewModel should be done via a properly defined property or function which returns an interface, this is to avoid tight coupling.

Should it call some method of the Model which then communicates changes back to the View Model/View?

There is no problem with a command accessing the Model from the ViewModel. The command can update properties on the ViewModel or Model, and via the magic of databinding and property notifications these updates can be reflected back in the UI.

1
votes

The approach with the static commands is usually taken with RoutedCommands or commands which have a broad application which is not coupled to the state of any specific object. If you use dynamic implementations of ICommand which get the their methods passed to them in the constructor they are usually instances of the ViewModel to which that command is relevant.

Those commands can act on the ViewModel and Model, how you do this is up to you, i often just call a respective method on the VM from the command's Execute to keep the command initialization code concise.

I cannot really tell you what the best approach is, maybe someone else has more insight on what works best when and why.