63

I like MVVM. I don't love it, but like it. Most of it makes sense. But, I keep reading articles that encourage you to write a lot of code so that you can write XAML and don't have to write any code in the code-behind.

Let me give you an example.

Recently I wanted to hookup a command in my ViewModel to a ListView MouseDoubleClickEvent. I wasn't quite sure how to do this. Fortunately, Google has answers for everything. I found the following articles:

While the solutions were helpful in my understanding of commands, there were problems. Some of the aforementioned solutions rendered the WPF designer unusable because of a common hack of appending "Internal" after a dependency property; the WPF designer can't find it, but the CLR can. Some of the solutions didn't allow multiple commands to the same control. Some of the solutions didn't allow parameters.

After experimenting for a few hours I just decided to do this:

private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
    ListView lv = sender as ListView;
    MyViewModel vm = this.DataContext as MyViewModel;

    vm.DoSomethingCommand.Execute(lv.SelectedItem);
}

So, MVVM purists, please tell me what's wrong with this? I can still Unit test my command. This seems very practical, but seems to violate the guideline of "ZOMG... you have code in your code-behind!!!!" Please share your thoughts.

Thanks in advance.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
JP Richardson
  • 38,609
  • 36
  • 119
  • 151
  • 1
    I've done this myself, I see no problem with it... – Jack Ukleja Jan 31 '10 at 10:00
  • 7
    ZOMFG CODE BEHIND!!!!!!! – Pierreten Mar 18 '10 at 01:17
  • 1
    I would agree with @Schneider -- this makes perfect sense. In fact the volume of plumbing code required to avoid using the so-called code-behind file still makes my head spin. – Ralph Shillington Mar 21 '10 at 18:52
  • I'm MVVM purist and I'm ok with it. The only difference is readability. However `ListView_MouseDoubleClick` event might be tricky, because the user might click outside an ListItem (e.g. scrollbar, border, padding, etc). You should better use ListViewItem.MouseDoubleClick. – Liero Aug 18 '22 at 06:58

7 Answers7

37

I think the fault lies in the purity requirement. Design patterns, MVVM included, are a tool in the toolbox, not an end unto themselves. If it makes more sense to break with the purity of the model for a well-considered case (and it clearly looks like you've considered this case), then break with the model.

If that works for you, and you don't believe it's an undue maintenance burden, then I'd say that nothing is wrong with what you've done. I think that you've clearly met the burden of proof for showing that this is a reasonable solution to your problem in spite of what a pure MVVM implementation might be.

(I consider this argument similar to the arguments for multiparadigm languages. While a Pure OO approach can be applied, sometimes doing things in a more functional way is more appropriate. While a Pure Functional approach can be applied, sometimes the trade offs show that OO techniques are more than worth the while.)

Greg D
  • 43,259
  • 14
  • 84
  • 117
  • 9
    Agreed, excessive purity, whether with MVVM or any other pattern, becomes counterproductive. I've seen people saying that you shouldn't even use value converters because your view model should be doing all the work -- a surefire recipe for tight coupling and architectural fail. MVVM is a (very) useful guiding principle to *how to think* in WPF so as to make your code simple and decoupled. It should never become a stick with which to beat your code into an unnatural mess of twisted complexity. – itowlson Jan 19 '10 at 01:49
  • 1
    The original question mirrors my own frustrations trying to do pure MVVM and I like your answer, however I'd like to point out that when learning a new pattern, particularly one as othello-like as MVVM (easy to learn, difficult to master), the best way to approach that learning is to do everything you can to force yourself into pattern purity. Only after you've learned the hard lessons can you gain the experience necessary to know when and how to break the pattern. – Randolpho Jul 16 '10 at 21:11
  • 1
    @Randolpho: You make a very good and cogent point, and I completely agree. I strive for purity of my understanding of the pattern in most of my personal projects, and certainly the small ones. I think MVVM in particular is tricky because there's so much gray area wrt what belongs in the view vs the view model. My personal opinion is that anything that doesn't affect application state stays out of the VM and in the view (maybe even the codebehind!), but am I then writing something more like an MVP? Dunno. Very subtle stuff! – Greg D Jul 16 '10 at 22:01
13

I agree with you that many MVVM-Command solutions are too complicated. Personally, I use a mixed approach and define my Commands in the View rather than in the ViewModel, using methods and properties from the ViewModel.

XAML:

<Window.Resources>
    <RoutedCommand x:Key="LookupAddressCommand" />
</Window.Resources>
<Window.CommandBindings>
    <CommandBinding Command="{StaticResource LookupAddressCommand}" x:Name="cmdLookupAddress" />
</Window.CommandBindings>

Code (View):

Private Sub cmdLookupAddress_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs) Handles cmdLookupAddress.CanExecute
    e.CanExecute = myViewModel.SomeProperty OrElse (myViewModel.SomeOtherProperty = 2)
End Sub

Private Sub cmdLookupAddress_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs) Handles cmdLookupAddress.Executed
    myViewModel.LookupAddress()
End Sub

It's not pure MVVM, but it simple, it works, it does not need special MVVM-command-classes and it makes your code much easier to read for non-MVVM-experts (= my co-workers).

Heinzi
  • 167,459
  • 57
  • 363
  • 519
10

Although I prefer not to write code-behind when using the MVVM pattern, I think it's OK to do it as long as that code is purely related to the UI.

But this is not the case here : you're calling a view-model command from the code-behind, so it's not purely UI-related, and the relation between the view and the view-model command is not directly apparent in XAML.

I think you could easily do it in XAML, using attached command behavior. That way you can "bind" the MouseDoubleClick event to a command of your view-model :

<ListView ItemSource="{Binding Items}">
   <local:CommandBehaviorCollection.Behaviors>
      <local:BehaviorBinding Event="MouseDoubleClick" Action="{Binding DoSomething}" />
   </local:CommandBehaviorCollection.Behaviors>

    ...
</ListView>

You can also easily access the selected item of the ListView without referring to it directly, using the ICollectionView interface :

private ICommand _doSomething;

public ICommand DoSomething
{
    get
    {
        if (_doSomething == null)
        {
            _doSomething = new DelegateCommand(
                () =>
                {
                    ICollectionView view = CollectionViewSource.GetDefaultView(Items);
                    object selected = view.CurrentItem;
                    DoSomethingWithItem(selected);
                });
        }
        return _doSomething;
    }
}
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
5

I believe that the goal of having "No code in the code-behind" is exactly that, a goal to reach for - not something that you should take as an absolute dogma. There are appropriate places for code in the View - and this isn't necessarily a bad example of where or how code may be simpler than an alternative approach.

The advantage of the other approaches you list, including attached properties or attached events, is that they are reusable. When you hook up an event directly, then do what you did, it's very easy to end up duplicating that code throughout your application. By creating a single attached property or event to handle that wiring, you add some extra code in the plumbing - but it's code that is reusable for any ListView where you want double-click handling.

That being said, I tend to prefer using the more "purist" approach. Keeping all of the event handling out of the View may not effect the testing scenario (which you specifically address), but it does effect the overall designability and maintainability. By introducing code into your code behind, you're restricting your View to always using a ListView with the event handler wired - which does tie your View into code, and restrict the flexibility to redesign by a designer.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
2

Decoupling is one of the major feature of MVVM. If suppose you want to change say view or binded model to it. How much easy it is for your application?

Take an example where View1 and View2 both share the same ViewModel. Now will you implement the code behind method for both.

Also, suppose if you need to change the viewmodel for a view on later stage your command will get fail as view model is changed and statement

MyViewModel vm = this.DataContext as MyViewModel;

will return null and hence code crash. So there comes an extra burden to change the code behind also. These kind of scenarios will arise if you do it in this way.

Of course there are many ways to achieve the same thing in programming but which one is best will lead to best approach.

D J
  • 6,908
  • 13
  • 43
  • 75
  • 1. _"implement the code behind method for both."_ The OP didn't implement any logic in the evenhandler except calling command. Whether you do it in XAML or codebehind makes no difference except readability. – Liero Aug 18 '22 at 06:59
  • 2. _"change the viewmodel for a view"_: View is always tighly coupled to viewmodel, whether you use binding, or access VM from codebehind. Actually codebehind is more typesafe. Changing a VM (type) for a View is not a common practice. _"your command will get fail as view model is changed...will return null and hence code crash"_ - So would your XAML binding become invalid - and they are even harder to fix – Liero Aug 18 '22 at 07:01
2

What @JP describes in the original question and @Heinzi mentions in is answer is a pragmatic approach to handling difficult commands. Using a tiny bit of event handling code in the code behind comes in especially handy when you need to do a little UI work before invoking the command.

Consider the classic case of the OpenFileDialog. It's much easier to use a click event on the button, display the dialog, and then send the results to a command on your ViewModel than to adopt any of the complicated messaging routines used by the MVVM toolkits.

In your XAML:

<Button DockPanel.Dock="Left" Click="AttachFilesClicked">Attach files</Button>

In your code behind:

    private void AttachFilesClicked(object sender, System.Windows.RoutedEventArgs e)
    {
        // Configure open file dialog box
        Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
        dlg.FileName = "Document"; // Default file name
        dlg.DefaultExt = ".txt"; // Default file extension
        dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

        // Show open file dialog box
        bool? result = dlg.ShowDialog();

        // Process open file dialog box results
        if (result == true)
        {
            string filename = dlg.FileName;

            // Invoke the command.
            MyViewModel myViewModel = (MyViewModel)DataContext;
            if (myViewModel .AttachFilesCommand.CanExecute(filename))
            {
                noteViewModel.AttachFilesCommand.Execute(filename);  
            }
        }
    }

Computer programming is inflexible. We programmers have to be flexible to order to deal with that.

dthrasher
  • 40,656
  • 34
  • 113
  • 139
1

Commanding is for chumps. Real men wire up their entire ui to events in codebehind.

Pierreten
  • 9,917
  • 6
  • 37
  • 45