简体   繁体   中英

Does calling View Model methods in Code Behind events break the MVVM?

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.

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM