Wednesday, 16 October 2013

Using Blend behaviours in a Windows Store App

Previously I’ve demonstrated a sample PhotoViewer app that displays photo thumbnails on the MainPage, and that displays a selected photo on the PhotoPage. The app can also convert a colour photo to greyscale on the PhotoPage. In addition, the app serializes frame and page state during suspension, and deserializes it when the app reactivates following termination.

The app uses an attached behaviour on the GridView on the MainPage to execute a view model action in response to the ItemClick event firing on the GridView. This approach eliminates the need for an event handler in the code-behind of the MainPage class, and ensures that navigation is invoked from the view model class, where it can be easily tested.

With the release of Visual Studio 2013 Blend behaviours are now supported in Windows Store apps. A Blend behaviour adds some behaviour to an element in your app, and has a method that’s invoked when the behaviour is attached to a DependencyObject, and a method that’s invoked when it’s detached. An action contains only one method that is invoked when a certain condition is met, such an event being raised. The Behaviours SDK consists of the following actions and behaviours:

CallMethodAction: An action that calls a method on a specified object when invoked.

ChangePropertyAction: An action that will change a specified property to a specified value when invoked.

GoToStateAction: An action that will transition a FrameworkElement to a specified VisualState when executed.

InvokeCommandAction: Executes a specified ICommand when invoked.

NavigateToPageAction: An action that switches the current visual to the specified Page.

ControlStoryboardAction: An action that will change the state of the specified Storyboard when executed.

PlaySoundAction: An action that will play a sound to completion.

DataTriggerBehaviour: A behaviour that performs actions when the bound data meets a specified condition.

EventTriggerBehaviour: A behaviour that listens for a specified event on its source and executes its actions when that event is fired.

IncrementalUpdateBehaviour: A behaviour that allows incremental updating of ListView and GridView contents to support faster updating. By attaching this behaviour to elements in the ItemTemplate used by these views, some of the updates can be deferred until there is render time available, resulting in a smoother experience.

You can create your own action by adding a class to your project that derives from DependencyObject and implements the IAction interface. Similary you can create a behaviour by adding a class to your project that derives from DependencyObject and implements the IBehavior interface.

In this blog post I’ve replaced the attached behaviour that was previously used with a Blend behaviour that has the same effect.

Implementation

The first step is to add a reference to the Behaviours SDK to your project.

image

Then the Microsoft.Xaml.Interactivity and Microsoft.Xaml.Interactions.Core namespaces should be imported into the XAML of the page you want to use a Blend behaviour from.

<prism:VisualStateAwarePage
    ...
    xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"
    ...>

Previously in the app, the GridView used an attached behaviour to execute a view model action when the ItemClick event fires on the GridView. Here, a Blend behaviour and a Blend action is used to achieve the same effect.

<GridView Grid.Row="1"
          IsItemClickEnabled="True"
          ItemsSource="{Binding Photos}"
          Margin="140,0,0,0"
          SelectionMode="None">
    <interactivity:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="ItemClick">
            <core:InvokeCommandAction Command="{Binding PhotoNavigationCommand}"
                                      CommandParameter="{Binding Path=SelectedItem}"/>
        </core:EventTriggerBehavior>
    </interactivity:Interaction.Behaviors>
    ...
</Grid>

An EventTriggerBehavior is used which handles the ItemClick event. The InvokeCommandAction specifies the view model command that will be invoked when the event fires, and passes a parameter to the view model command. The overall effect is that when the ItemClick event fires on the GridView, the PhotoNavigationCommand in the MainPageViewModel class is invoked, with the PhotoNavigationCommand receiving the SelectedItem property of the GridView as a parameter.

public DelegateCommand<ItemClickEventArgs> PhotoNavigationCommand { get; private set; }
 
public MainPageViewModel(IRepository repository, INavigationService navigationService)
{
    _repository = repository;
    _navigationService = navigationService;
    PhotoNavigationCommand = new DelegateCommand<ItemClickEventArgs>(NavigateToPhoto);
}
 
private void NavigateToPhoto(ItemClickEventArgs parameter)
{
    var photo = parameter.ClickedItem as FileInformation;
    if (photo != null)
    {
        _navigationService.Navigate("Photo", photo.Path);
    }
}

In the MainPageViewModel class the PhotoNavigationCommand is a generic DelegateCommand of type ItemClickEventArgs. This is necessary so that the command receives the event data for the ItemClick event. In the MainPageViewModel constructor the PhotoNavigationCommand is initialiased to a new DelegateCommand that will execute the NavigateToPhoto method. This method gets the photo that was clicked on and uses the NavigationService to navigate to the PhotoPage where the clicked on photo will be displayed.

Summary

In this blog post I’ve replaced the attached behaviour that was previously used to execute a view model action with a Blend behaviour that executes a view model command. The advantage of this approach is that you use what’s provided by the Blend SDK, rather than having to write your own attached behaviour, which may introduce errors, and associated unit tests.

The sample app can be downloaded here.

1 comment:

  1. Nicely explained. Thanks. One correction: When using InvokeCommandAction setting the CommandParameter property is probably not supported. Strange thing is that you set the command parameter to SelectedItem but receive an ItemClickEventArgs object (I would expect to get a FileInformation object instead for your example). Actually we can remove CommandParameter="{Binding Path=SelectedItem}" completely and it works anyway.

    ReplyDelete