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:
- 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.
- 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.