MVVM event to command binding #1

One of the core features of XAML is Data Binding. The {Binding ...} syntax helps us enforcing the MVVM pattern in that it allows displaying data stored in a Viewmodel to the user, and writing back data which was input by the user to the Viewmodel.

What about reacting to user-triggered events in contrast to data input? Traditionally, we’d subscribe to one of the events each user control offers, create an event handler method in code-behind, and process the desired logic in there. How does that comply with MVVM?

Let’s take a look at a few different cases of how to handle user events:

  1. User actions that shall have only visual effects can often be handled purely within XAML, e.g. through the element’s VisualStateManager. For example, to change a button’s inner color on the MouseEnter event, use its MouseOver visual state:
    <Style x:Key="MyButton" TargetType="{x:Type Button}">
    	<Setter Property="Template">
    		<Setter.Value>
    			<ControlTemplate TargetType="{x:Type Button}">
    				<Grid>
    					<Ellipse x:Name="ellipse" Fill="Yellow"/>
    					<ContentPresenter
    						HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    						VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    					<VisualStateManager.VisualStateGroups>
    						<VisualStateGroup x:Name="CommonStates">
    							<VisualState x:Name="Normal"/>
    							<VisualState x:Name="MouseOver">
    								<Storyboard>
    									<ColorAnimation
    										To="Red"
    										Duration="0"
    										Storyboard.TargetName="ellipse"
    										Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"/>
    								</Storyboard>
    							</VisualState>
    							<VisualState x:Name="Pressed" />
    							<VisualState x:Name="Disabled"/>
    						</VisualStateGroup>
    					</VisualStateManager.VisualStateGroups>
    				</Grid>
    			</ControlTemplate>
    		</Setter.Value>
    	</Setter>
    </Style>
    
  2. A similar result can be achieved using Triggers. Also this approach needs no code-behind:
    <ControlTemplate TargetType="{x:Type Button}">
    	<Grid>
    		<Ellipse x:Name="ellipse">
    			<Ellipse.Style>
    				<Style TargetType="Ellipse">
    					<Setter Property="Fill" Value="Yellow"/>
    					<Style.Triggers>
    						<Trigger Property="IsMouseOver" Value="True">
    							<Setter Property="Fill" Value="Red"/>
    						</Trigger>
    					</Style.Triggers>
    				</Style>
    			</Ellipse.Style>
    		</Ellipse>
    	</Grid>
    </ControlTemplate>
    
  3. In more complex settings, it might be simpler to rely to a code-behind event handler. This can be the case when handling two separate elements:
    <StackPanel>
    	<Button MouseMove="Button_MouseMove">click me</Button>
    	<TextBlock x:Name="text">watch me</TextBlock>
    </StackPanel>
    
    private void Button_MouseMove(object sender, MouseEventArgs e)
    {
    	text.Visibility = Visibility.Hidden;
    }
    

    If the event has only visual effects, this still does not break the MVVM pattern, since everything within the event handler is View logic – no need to bother the Viewmodel with it!

  4. What if the Viewmodel shall be notified about a user event, e.g. to trigger some business logic function? Fortunately, most user controls offer a Command property which we can bind to:
    <Button Command="{Binding SomeCommand}">click me</Button>
    

    A big advantage of this approach is that MVVM Commands even allow disabling user controls from within Viewmodel logic through defining a CanExecute condition (just a side note – we won’t go into detail about this mechanism this time).

  5. The problem of the Command-binding approach is that each of the default user controls provides only one Command property, which resembles the most commonly used event. What if we need to react to any other event? It’s not even mandatory to offer a Command property, so 3rd party controls (which, for example, were not designed to be used with MVVM) might not support Command binding at all.

    In some cases, Viewmodels can still be notified about user events. For example, to react to the SelectionChanged event of a list control, we could bind a Viewmodel property to the list’s SelectedIndex property, and trigger our custom logic in the property’s setter:

    private int _selectedIndex;
    public int SelectedIndex
    {
    	get { return _selectedIndex; }
    	set
    	{
    		if (!_selectedIndex.Equals(value))
    		{
    			_selectedIndex = value;
    			SelectionHasChanged();
    		}
    	}
    }
    
  6. After all, there are still cases which are not covered by the approaches listed above – one example might be a drag and drop operation, which typically includes several events some of which might trigger some business logic. The following line of code won’t work, since we can’t use the Binding markup extension on events (an exception is UWP’s x:Bind syntax, but we’ll come to the in a later part of this article):
    <ListBox Drop="{Binding DropCommand}"/>
    

    In those cases, we typically create an event handler method in code-behind, and execute a Viewmodel Command from there:

    private void ListBox_Drop(object sender, DragEventArgs e)
    {
    	var vm = DataContext as MainViewmodel;
    	if (vm != null)
    	{
    		if (vm.DropCommand.CanExecute())
    		{
    			vm.DropCommand.Execute();
    		}
    	}
    }
    

There exist several problems with the last approach listed above: First of all, we need to access the control’s Viewmodel somehow (e.g., through its DataContext property). In addition, the fact that the event handler is hidden in code-behind makes our code less readable (compared to code purely written in XAML). Finally, if we are forced to switch to C# code anyway, we might easily be tempted to handle the desired business logic in there, instead of tediously referencing the Viewmodel – and this finally breaks the MVVM pattern.

So, there must be another way of coupling user control events and Viewmodel actions – in fact, there are several ways of doing so, each of which I’d like to discuss in one of the following blog posts. Let’s start with the universal but rather lengthy behavior / trigger based approach!