views:

1134

answers:

6

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.

+24  A: 

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
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
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
@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
True.dat! :) ....
Randolpho
+10  A: 

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
That's clever :)
cwap
+3  A: 

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
+4  A: 

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
I meant to put this link as well. But this is one of those that renders the WPF designer unusable. At least it did for me. Maybe I did something wrong?
JP
<Insert mandatory joke about the WPF designer being unusuable by default here.>
Greg D
Ah yes, it does breaks the designer... I had forgotten that. Anyway, as Greg D humorously points out, this designer almost never works ;). I never use it, I only write XAML...
Thomas Levesque
My WPF productivity went way up when I started pretending that the designer doesn't exist.
Robert Rossney
Using the new Blend Behaviors, attached behaviors for this sort of thing no longer need to break the designer.
wekempf
+1  A: 

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

Pierreten
Nope, *REAL* men write their algorithms in XAML.
Robert Fraser
Obligatory xkcd cartoon about Real Programmers: http://xkcd.com/378/
dthrasher
+1  A: 

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