Sunday, March 21, 2010

Commands as XAML Resources

I’m watching Nikhil Kothari’s talk at MIX 2010 while I write. As with all things Nikhil, you owe it to yourself to take a look.
The talk is nominally about developing with WCF RIA Services. It’s really about programming style. Most RIA Services demos are 100% drag-and-drop. You might think that’s the only way or the preferred way to write a RIA Services application.
Not so!  … and Nikhil is determined to show a different path. A standing ovation from me for bucking that trend … while keeping it all accessible to a general development audience.
I particularly liked the moment around the 31st minute when he uses SketchFlow to show how the View relates to a ViewModel.
Scoot ahead to 35:40 and you’ll find Nikhil wiring ViewModel actions to View buttons via Commands that he’s instantiated as resources in the View.
I’m intrigued by this approach even if I’m not completely sold on it.

The Problem

Silverlight lacks the means to directly bind view events (e.g., button clicks) to ViewModel actions (e.g., “Load Books”). That omission has spurred a variety of solutions: event handlers in code-behind, ViewModel commands wired by attached behaviors, and auto-wire-by-convention (Caliburn-style) to name a few.
Nikhil has found another way to do it. We can leverage data binding and declare the hook-up in XAML using a simple resource rather than the more obscure behavior. It is quite seductive. Let me elaborate.

Button Binding

The button has a Command property which can be bound to an implementation of ICommand.
In MVVM we strive to bind the View to members of the ViewModel. It seems natural for the ViewModel to expose ICommand implementations that we can bind to buttons in the View. I’ve done this myself … I hate to admit.
Picking up on Nikhil’s example, our BookViewModel could present a “LoadBooksCommand” property that delivers an ICommand wrapper around the VM’s private “CanLoadBooks” and “LoadBooks” methods.
To support this we write code in the ViewModel like so:
    private ICommand _loadBooksCommand = new Command(LoadBooks, CanLoadBooks);
    public ICommand LoadBooksCommand { get { return _loadBooksCommand; } } 
Yuck. The ViewModel author is interested in “LoadBooks” and “CanLoadBooks”. These are the meat of the matter.
The ICommand is a View artifact that has crept into the ViewModel. The Command(…) isn’t even an instance of a .NET class; we have to get it as a helper from some framework ... Prism perhaps. The whole business is distracting kruft and a PITA.
Many of us just put up with it, writing those command pairs over and over.
Nikhil says “Let’s not pollute the ViewModel this way. Let’s refactor the Command back to the view where it belongs”. Our ViewModel will expose “CanLoadBooks” and “LoadBook”s publically just as we would any bindable property. No more ICommand nonsense.

Command Resources

In his SilverlightFx framework he’s defined a “Command” class that we can instantiate in XAML.
We create Command instances as resources in our view as follows:
    <fx:Command x:Key=”loadCommand” Target=”{Binding}” Method=”LoadBooks” />
Notice that the target is the default binding which will be the ViewModel and the Method name corresponds to the method of that name in the ViewModel (I’m guessing he can infer the CanLoadBooks method as well).
Finally, we bind the “loadButton” Command property to the “loadCommand” resource. In the video, Nikhil uses the Cider designer for this purpose (so much for “no drag-and-drop”). You might prefer to tweak the XAML yourself:
    <Button x:Name=”loadButton” Command=”{StaticResource loadCommand}" ...  />

What’s Not To Like?

I guess it’s ok. Nikhil is making the better of a bad situation, namely, that we cannot yet bind View events to ViewModel methods.
We require a trick shot. We bank the binding cue ball from the button’s Command property off of a static resource at the top of the XAML file and into the ViewModel “LoadBooks” hole. I am not happy about that. I’d prefer to bind the button directly to my ViewModel. On the other hand, I dislike the ICommand crap in my ViewModel. If I must choose between the two, I’m reluctantly inclined toward Nikhil’s approach.
Of course they both breakdown when you need to wire a ViewModel method to anything other than a button Command property. What do you do for the SelectionChanged event of a ComboBox? You can’t bind to that as you can “Button.Command”. You’ll have to reach for a different trick – an attached property perhaps – to do what is essentially the same thing.

Bind By Convention

That’s why I’m increasingly enamored of Rob Eisenberg’s “bind-by-convention” approach which he showed at MIX. I can write
  <Button x:Name=”LoadBooks” />
and Rob’s little framework will both find and wire up my ViewModel’s “LoadBooks” and “CanLoadBooks” methods.
If my ComboBox looks like this:
 <ComboBox x:Name=”Books” />
and if my ViewModel has "Books", "SelectedBook" and "BooksChanged" methods, he'll bind the ComboBox's ItemsSource, SelectedItem and SelectionChanged events for me.
There’s no fuss, no muss whether I’m binding Buttons, ListBoxes, TextBoxes, whatever. And I can always go old-school when the naming conventions fail me. Pretty sweet.
A minor downside with Rob’s approach is the lack of tooling support. Intellisense doesn’t kick in when I’m setting a control’s Name property. I can live with that … especially because his full framework comes with diagnostics that tell me which expected bindings are missing at runtime.

Summing Up

I’m all too used to the ICommand-in-ViewModel and I know I don’t love that. I have a feeling that Nikhil’s Command Resource is a better way. It’s certainly more elegant.
Bind-by-convention is the most appealing choice. But I’ll withhold final judgment until I have more experience with it.
You’ll want to watch Nikhil’s talk in any case to get a more rounded view of your choices. He ranges over more ground than commanding … commanding is perhaps 10 minutes of a one hour talk. Check it out.

Comments on this post are closed.

13 comments:

Unknown said...

I haven't watched Nikhil's video yet, but does his solution include a means of detecting a change to the can-execute status of a command? In other words, how would one go about raising the command's CanExecuteChanged event if the command is a XAML resource?

Josh

Ward Bell said...

I haven't downloaded the code yet.

I assume that, because his Command resource implements ICommand, the act of binding the button to that resource is sufficient. The button.Command binding will hook up to the command's CanExecuteChanged as well as attach the button's IsEnabled to the CanExecute method. You'd expect that; it's kind of the point of ICommand.

The detail nagging at me is how the command in the resource knows about the ViewModel when it (the command) is instantiated prior to the setting of the View's DataContext. I'm sure he's figured out how to ensure that his Command completes the wiring to the ViewModel when the parent View's DataContext is set.

Nikhil Kothari said...

The Command has a Target property which is bound to the DataContext (explicitly in the demo, but implicitly by default as I move this into the framework as a feature). If/when DataContext changes, the Command picks that up.

When the Command's Method property is set to Foo, it looks for a CanFoo property, and also subscribes to property change notifications for CanFoo, and in turn raises the ICommand.CanExecutePropertyChanged event. The ViewModel simply raises normal INPC events.

Michael L Perry said...

Since the View Model is intended for the view, I see no problem with ICommand entering into it. Why are you so relunctant to use that technique?

dani calbet said...

I disagree with your approach: Commands have nothing to do with presentation so is quite strange to pretend that defining them in the view has any advantatge.

I think origin of the confusion with commands is very simple:
WPF introduced the concept of commands and commandBindings, so we can easily have multiple pieces of UI controls firing the same command. This is interesting if we are working in a classical "code-behind" style, but is completely superfluos if we have the power of a viewModel to define our commands. 

The situation is much more clear in Silverlight, simple because it doesn't have commandbindings. WPF people has to unlearn everything about commandBindings first, while Silverlight people are using much more simple and direct approaches.

The "Bind by convention" approach may have advantatges, but please don't compare it with overcomplicated approaches.

Michael L Perry said...

Maybe it would be more palatable to put commands in the View Model if ICommand wasn't in the System.Windows.Input namespace. Like Dani says, the interface itself has nothing to do with presentation. The Command Pattern [GOF] has many uses.

I disagree with Dani's assertion that Silverlight makes the situation clearer by not directly supporting commands. That decision just forces us to do extra work to create portable code.

It would be acceptable for both WPF and Silverlight to take a dependency on a reusable ICommand interface, as long as that interface were defined elsewhere. But it is not acceptable for other consumers of ICommand to take a dependency upon WPF or Silverlight. Perhaps that is where the original aversion to commands in the View Model comes from.

In any case, hats high to your audacious post.

Dani Calbet said...

to Michael:
What I say is that the situation is clearer in Silverlight because it doesn't has CommandBindings, not commands. The commandBinding thing is what is superflous if you have viewmodels. SL 4 has a nice, simple command support.

br1 said...

Keeping the ICommand in the View is good, but defining it far away at the beginning of the Xaml is bad. How about:

<Button>
<Button.Command>
<fx:Command Target=�{Binding}� Method=�LoadBooks� />
</Button.Command>
</Button>

Dave said...

I'll put my vote to the last comment of using Button.Command.

Kasimier Buchcik said...

“Let’s not pollute the ViewModel this way. Let’s refactor the Command back to the view where it belongs”.

- If Nikhil's way of thinking about the Command pattern is restricted to UIs, then he shall be free to follow his way. But before spreading the word, please review commanding in e.g. Prism or Caliburn in order to check if his approach covers some more complex use-cases.

- Would you rather implement an ICommand once in your view model, or pollute every single view with Nikhil's command definitions?

- Do you want the designer (person) to define Nikhil's command definitions? The designer (person) knows what methods to choose for a command?

- If you guys really don't like the ICommand and want methods on your view model instead, then for Zeus' sake, vote to making the view model a first class citizen in Silverlight/WPF.
E.g. the coder of the view model could then annotate relevant methods/properties with an attribute in order to be picked up by the designer (tool) and transformed into a either Nikhil's commands or implicit commands (not visible).

While I'm at it: Nikhil's team could also think about refactoring the DomainDataSource back to the ViewModel where it belongs :-)

Cheers

Ward Bell said...

Hey K. - I agree with your point about not putting this kind of thing in the XAML. It was interesting but unconvincing.

What I like is not having all the extra ceremony in your VM to make ICommands. The VM provides the methods(.) Wrapping them in ICommands is a distraciton. Caliburn does away with that via conventions.

I don't think it is fair to say that Nikhil wanted designers to write the command resources. He suggested that they *use* them.

I think Nikhil would *flip* if he heard his name associated with the DDS. It's not *his* team either.

Philipp Munin said...

I'm trying to implement the same on Silverlight 5 and I see that Command.Target binding does not work, because Command declared in resources does not inherit DataContext. In WPF there is Freezable class that did the job, but Silverlight does not have this class. I didn't find how an answer how to make it work in Silverlight. Help is appreciated.

Philipp Munin said...

I'm trying to implement the same on Silverlight 5 and I see that Command.Target binding does not work, because Command declared in resources does not inherit DataContext. In WPF there is Freezable class that did the job, but Silverlight does not have this class. I didn't find how an answer how to make it work in Silverlight. Help is appreciated.