A WinRT behavior to mimic EventToCommand (take 2)

2 minute read

(Updated with sample code January 19 2013)

Some time ago I posted a WinRT behavior to mimic EventToCommand. This behavior cunningly looked for the command name in the DataContext, but it occurred to me (and some blog post commenters as well) that it actually might be more logical skip all that skullduggery and let the developer bind to a command in stead of letting her/him provide its name as string and then go look for it.

Anyway, not wanting to break compatibility I added a new behavior to my win8nl library on CodePlex, very originally called EventToBoundCommandBehavior.

It’s working is very similar to the original EventToCommandBehavior. There are two notable differences:

  • The Command property is now no longer of type string but ICommand
  • The FireCommand method is now very simple:
private void FireCommand()
{
  if (Command != null && Command.CanExecute(CommandParameter))
  {
    Command.Execute(CommandParameter);
  }
}

Which goes to show that you can be too clever.

If you want to replace the original behavior for the new one – say, you used it like this

<TextBlock Text="TextBlock" FontSize="48">
  <WinRtBehaviors:Interaction.Behaviors>
    <Behaviors:EventToCommandBehavior Event="Tapped" 
      Command="TestCommand" 
      CommandParameter="{Binding TestProperty, Mode=TwoWay}"/>
  </WinRtBehaviors:Interaction.Behaviors>
</TextBlock>

Just go to the places marked red/underlined:

<TextBlock Text="TextBlock" FontSize="48">
  <WinRtBehaviors:Interaction.Behaviors>
    <Behaviors:EventToBoundCommandBehavior Event="Tapped" 
      Command="{Binding TestCommand}" 
      CommandParameter="{Binding TestProperty, Mode=TwoWay}"/>
  </WinRtBehaviors:Interaction.Behaviors>
</TextBlock>

Those who want to see the full source code of the new (and largely simplified) behavior can do so at http://win8nl.codeplex.com/SourceControl/changeset/view/20896#395786

Update: I’ve added some more sample code as a lot of people seem to struggle with the correct use of this behavior. Lots of time I get program problems like this: “I have a list in my view model and a command yet my command is not fired when then I tap the item”. The view model usually contains these properties:

public ObservableCollection Phones { get; set; }

public ICommand TappedCommand
{
  // code omitted
}

and the XAML is like this:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" 
  DataContext="{StaticResource DemoViewModel}">

  <GridView ItemsSource="{Binding Phones}" SelectionMode="None" 
     IsTapEnabled="True">
    <GridView.ItemTemplate>
      <DataTemplate>
        <Grid Width="350">
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <WinRtBehaviors:Interaction.Behaviors>
            <Behaviors:EventToBoundCommandBehavior 
              Command="{Binding TappedCommand}" 
              CommandParameter="{Binding}" Event="Tapped" />
          </WinRtBehaviors:Interaction.Behaviors>

          <TextBlock Text="{Binding Brand}"/>
          <TextBlock Text="{Binding Type}" Grid.Column="1"/>
        </Grid>
      </DataTemplate>
    </GridView.ItemTemplate>
  </GridView>
</Grid>

I this case, the programmer has lost track of what’s the exact data context. Happens all the time, even to me. I’ve used colors to depict the data context changes.

  • In the black code, DemoViewModel is the data context
  • In the red code, the ObserservableCollection Phones is the data context
  • In the blue code, a single Phone is the context.

So what this code tries to do is to call a command TappedCommand on class Phone in stead of DemoViewModel.So either the command needs to be in “Phone”, or the behavior’s binding must be updated like this:

<WinRtBehaviors:Interaction.Behaviors>
  <Behaviors:EventToBoundCommandBehavior 
    Command="{Binding TappedCommand, Source={StaticResource DemoViewModel}}" 
    CommandParameter="{Binding}" Event="Tapped" />
</WinRtBehaviors:Interaction.Behaviors>

Full working example can be found here.