16

I wonder if that would break the MVVM pattern and, if so, why and why is it so bad?

WPF:

<Button Click="Button_Click" />

Code Behind:

private void Button_Click(object sender, RoutedEventArgs e)
{
    ViewModel.CallMethod();
}

View Model:

public void CallMethod()
{
    // Some code
}

IMHO, it keeps the code behind quite simple, the view model is still agnostic about the view and code behind and a change to the view doesn't affect the business logic.

It seems to me more simple and clear than Commands or CallMethodAction.

I don't want the kind of answer "it is not how it should be done". I need a proper and logical reason of why doing so could lead to maintenance or comprehension problems.

Sébastien
  • 1,667
  • 3
  • 20
  • 31
  • 1
    You would usually bind the Button's `Command` property to an `ICommand` property in the view model, which executes the method. However, doing it the way you're showing here doesn't break anything. – Clemens Oct 20 '16 at 17:32
  • It's nice for the view's interaction with the viewmodel to be as duck typed as possible. There are cases where you'd like to be able to use a given view with two unrelated viewmodels that happen to have properties of the same name and types that'll both work with the controls they're bound to. But very often, views are highly specialized and that's just not going to be a consideration. In those cases a strongly typed `ViewModel` property is common practice. Multiple views per viewmodel is far more common than multiple *unrelated* viewmodels per view. – 15ee8f99-57ff-4f92-890c-b56153 Oct 20 '16 at 17:41

2 Answers2

16

Nope, this is perfectly fine.

It's the View's job to handle user input and interact with the ViewModel. A button click event-handler, which calls a method of the ViewModel in response, falls quite cleanly into this role.

What you have posted is clean, readable, efficient, maintainable, and fully in the spirit of the MVVM design pattern.

Now, in a more general sense, what you really want to ask is: "why choose ICommands, vs Event Handlers for MVVM?" Well, you certainly wouldn't be | the | first.

Community
  • 1
  • 1
BTownTKD
  • 7,911
  • 2
  • 31
  • 47
  • Thanks for your answer and the links for further insights. – Sébastien Oct 20 '16 at 19:59
  • Classes should not be tightly coupled to each other. If you are talking about XAML, then yeah, that needs to know what property to bind too, but absolutely code behind should NOT know about the VM. – SledgeHammer Oct 20 '16 at 23:42
  • 7
    @SledgeHammer I disagree entirely. Views can be implemented in code-behind or XAML, it doesn't matter. The crux of MVVM is that "Views know about ViewModels, but ViewModels don't know about VIews." The coupling is one-way, which means the ViewModels are fully portable and reusable, while Views are not. – BTownTKD Mar 16 '17 at 13:40
2

No, it doesn't break MVVM, as long as you don't introduce logic that more appropriately belongs in the viewmodel.

It has the potential to reduce the clarity, IMO, because it breaks the view into XAML and c# files that are tightly coupled and where you can't see everything going on in one place. I find it easier to have zero code behind because it means less context switching when working on the view.

It can also make it more challenging to work in an environment where your UI designer isn't a C# programmer, because then two different people are maintaining the tightly-coupled files.

Edit

Here's an example of what I mean. This is from a weekend project I did to implement Minesweeper in WPF for fun and experience. One of my biggest WPF challenges was mouse input. For anyone who hasn't wasted time on the game before, the left mouse click reveals a cell, the right mouse button toggles a flag on the cell, and the middle mouse button will (conditionally) reveal adjacent cells.

I first started by considering using System.Windows.Interactivity's EventTrigger along with InvokeCommandAction to map events to commands. This sort-of worked for the right mouse button (wasn't a true click event, but a MouseRightButtonUp) but it wouldn't work at all for the middle mouse button which had no specific actions, only the generic MouseDown/MouseUp. I briefly considered prism's variation on InvokeCommandAction which could pass the MouseButtonEventArgs to its handler, but that very much broke the MVVM concept and I quickly discarded it.

I didn't like the idea of directly putting event handlers in the code-behind because that tightly coupled the action (the mouse click) and the response (revealing cells, etc.). It also wasn't a very reusable solution - every time I wanted to handle a middle click I'd be copying, pasting, and editing.

What I settled on was this:

<i:Interaction.Triggers>
    <mu:MouseTrigger MouseButton="Middle" MouseAction="Click">
        <i:InvokeCommandAction Command="{Binding Path=RevealAdjacentCells}" />
    </mu:MouseTrigger>
</i:Interaction.Triggers>

In this case, there's no code in the code behind. I moved it into a MouseTrigger class I created that inherits from System.Windows.Interactivity.TriggerBase which, while being view-layer code, isn't part of any specific view, but a class which any view could utilize. This handler code is as agnostic as possible as to what kind of element it's attached to - anything derived from UIElement will work.

By leveraging this approach, I gained two key things over doing this in the event handlers on the code-behind:

  1. There's a loose coupling between the event and the action. If I had a UXD working on the UI, they could change what mouse button the command was associated to by just editing a line of XAML. For example, swapping right and middle mouse buttons is trivial and requires no .cs changes.
  2. It's reusable on any UIElement, not tied to any particular one. I can pull this out anytime I need to solve this kind of problem in the future.

The main drawback here is that it was initially more work to set up. My MouseTrigger class is more complex than the event handlers by themselves would be (mainly around properly handling dependency properties & changes thereof). XAML can also often be rather verbose for something that would seem simple.

PMV
  • 2,058
  • 1
  • 10
  • 15
  • Good point about the separation of powers. But, even if the designer doesn't have to care about the code behind, he still have to have a good understanding of Commands or CallMethodAction which has nothing to do with UI design so I don't find it more difficult to create the events and tell him their names and meanings. – Sébastien Oct 20 '16 at 19:57
  • It's true that some level of understanding of commands is needed. However, the command is less tightly coupled. What if the UI designer decides to delete the button and run the action when a list changes? At the very least, Button_Click should be renamed. Or if they want the same action to be available on a menu, toolbar, or mouse action? You can get away with a generic event handler in some cases, until you need to use event arguments. It becomes more complex when you need to trigger something on a sequence of events. If you like, when I'm home from work I can add a brief recent example. – PMV Oct 20 '16 at 20:42
  • A change to the event name is the same as a change to a command name, right? If you need to use event arguments, doesn't the commands begin to be tightly coupled too? Can I easily move a command from a MouseMove to a Click, if the command relied on the mouse position? It seems to me that the workload will be quite similar if a change has to be made. Plus, in the case of using exclusively events, you can convert event arguements (which are the responsibility of the view) to an agnostic method argument. – Sébastien Oct 20 '16 at 21:09
  • I definitely agree that you must handle any event argument related pieces in the view layer - but not necessarily in the code-behind for a specific view. And you're right, any change to the command names do require changes in the XAML, but in my experience, the existence of, say, a CalculateShipping command is less fluid than the mechanism by which the UI invokes the command (is it a button press, is it filling in a valid ZIP/post code, etc.). I prefer loose coupling around the pieces most likely to change. Edited my answer to show an example. – PMV Oct 20 '16 at 23:00