原文地址 :
http://blog.roboblob.com/2010/01/26/binding-ui-events-from-view-to-commands-in-viewmodel-in-silverlight-4/


Today we will touch another problem that people starting with MVVM very often fail to address properly:

Handling the UI Events of the View in the ViewModel while avoiding placing any logic in code behind of the View.

So our design goals for this post are:

  • We want to be able to wire-up UI Events to the commands in ViewModel via DataBinding in Xaml without any code behind in the View
  • View should not be aware of the ViewModel’s type or any other details of its existence – View should just use Silverlight’s DataBinding to access its ViewModel public properties (its DataContext) regardless of what is actually set to be there
  • We want to be able to handle any event that occurs in View (that includes not only Button clicks but also Mouse events,  Drag and Drop events, Loaded events etc).

When i was trying to solve this problem, i made a mistake.

I coupled the View with ViewModel,  made the View aware of the ViewModel via interface of the ViewModel.

View held the instance of ViewModel in its DataContext.

And then when some event fires in the View i would call a ViewModel method in code behind of the View (accessing it via local variable holding the ViewModel instance casted to interface that ViewModel is implementing).

While this approach works for all events in the View because you can create event handler for any View event, it has many drawbacks.

Main problems are:

  • View has to know about the ViewModel and it has to have a instance of the ViewModel injected somehow
  • we have to create event handlers in the View code behind .cs file for each event (this can be really boring, and im lazy to do repetitive code so that was really hard for me).

I didn’t like this solution at all and I immediately abandoned it.

Then i tried searching for possible solutions on the web and found the magical Expression Blend Samples.

Microsoft introduced Behaviors in Silverlight 3 with Blend 3 to help UI designers to have a flexible new way to add interactivity to applications.

Behaviors allow interactivity to be added to elements directly on the design surface in XAML without having to write additional code.

I will not go into details on Behaviors since there are tons of resources on the web on the subject,  you can read the original Blend 3 Behaviors announcement to get more info.

The great thing about Behaviors is that they introduced Triggers and very handy  EventTrigger and TriggerAction classes that are perfect match for our MVVM events scenario.

As you can read on the MSDN link EventTrigger  ‘Represents a trigger that applies a set of actions … in response to an event’.

So we can create a EventTrigger for some UI event in our View that will fire a custom TriggerAction that will call a method on our ViewModel with some parameters.

On the mentioned Expression Blend Samples page there is already implemented basic class for this – its called InvokeDataCommand and we will just customize it and extend (generalize it) so it serves our needs.

In the end we want to accomplish something like this in our View’s Xaml:

1 <Interactivity:Interaction.Triggers>
2     <Interactivity:EventTrigger EventName="MouseMove">
3         <TriggerActions:MapMouseEventToCommand Command="{Binding Path=ShowMousePositionCommand}" />
4     </Interactivity:EventTrigger>
5 </Interactivity:Interaction.Triggers>

So this is instructing our View to call the command called ShowMousePositionCommand on its ViewModel when mouse pointer is moved over some control.

So lets get started!

First we need to create a command class that we will be able to call from our Events.

Silverlight has only partial support for commanding.

Until Silverlight 4 ICommand interface was available – but without any real support or use.
In Silverlight 4 this interface is supported by Button type controls in again partial way:  in ButtonBase class from which all button-like controls inherit, there are two properties: Command and CommandParameter that you can point to a command that implements ICommand interface and pass it some parameter.

This can be done via DataBinding and when you click the button this command will be executed with the specified parameter. No code solution, pure XAML!
The problem is that Silverlight 4 Beta does not offer any implementation of the ICommand interface so we have to do this by ourselves.

Here is the ICommand interface:

1 namespace System.Windows.Input
2 {
3     public interface ICommand
4     {
5         bool CanExecute(object parameter);
6         void Execute(object parameter);
7         abstract event EventHandler CanExecuteChanged;
8     }
9 }

相关文章: