MVVM, Windows Phone 7

WP7 ListBoxItem Animation on bound data changes (MVVM)

by Peter Daukintis

Recently, I found myself needing to animate a List Box Item in response to it’s underlying bound data changing in a Windows Phone 7 application. Seems like this would be a fairly common requirement. I should add that a satisfying solution would be one that would adhere to an MVVM approach, be testable and maintain a separation between view and data. Anyway, I had to do a little bit of digging around to get a solution so it may save someone else some time.

I started out creating a new project using the ‘Windows Phone Application’ template.

 

NewWP7App 

I then cobbled together a view model like so,

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace ListBoxDataChange
{
    public class ViewModel
    {
        public ViewModel()
        {
            Data = new ObservableCollection<MyItem>
                       {
                           new MyItem { Name = "vdfskjavnfhvnfhn" },
                           new MyItem { Name = "vdfskjavnfhvnfhn" },
                           new MyItem { Name = "vdfskjavnfhvnfhn" },
                           new MyItem { Name = "vdfskjavnfhvnfhn" },
                           new MyItem { Name = "vdfskjavnfhvnfhn" },
                           new MyItem { Name = "vdfskjavnfhvnfhn" },
                       };
        }
        public ObservableCollection<MyItem> Data { get; set; }
    }

    public class MyItem : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private string _name;

        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }
    }
}

For testing purposes only I have created an ObservableCollection of a custom type (MyItem) which implements INotifyPropertyChanged on it’s Name property. This will ensure that the Silverlight data binding mechanism will detect both changes to the collection i.e. additions, deletions, etc and also changes to the contained data.

The next step to set the test up is to create the phone ui. To do this I opened the solution in Expression Blend for WP7 Beta and dragged a list box into the ContentGrid provided by the project template. Right-click the list box and use the Auto Size > Fill function to expand the list box to fit it’s container:

autosize

Next build the solution to ensure that our data objects are available to Blend for binding. Then select the PhoneApplicationPage in the objects and timelines panel and find it’s DataContext property in the Properties pane. Click on the small square next to the property and select Data binding to launch the ‘Create Data Binding’ dialog. Select the option ‘CLR Object’ and choose the ViewModel class created earlier.

databindingdlg 

This binds the View Model to the page, so we now need to bind the View Model’s collection to the ListBox ItemSource property. To do this in Blend right click the ListBox in the Objects and Timelines Panel and select ‘Data Bind ItemsSource to Data…’. In the ensuing dialog choose the DataContext tab and select the Data property here to complete the binding.

 

create_db

 

This will result in the following since we have not yet edited the ListBoxItem Data Template:

listboxbeforetemplateedit

So now we need to edit the template, so first right-click the list box and select ‘edit additional templates’ and then ‘edit generated items (itemtemplate)’ and choose ‘create empty’.

templatemode

 

This puts Blend into template editing mode and easily allows you to see in place the changes you are making.

Add a TextBlock to the empty Grid and bind its Text property to the MyItem Name property. This can be done by locating the Text property in the Properties panel and clicking the small square, select data binding and selecting the Name property.

 

So, now the scene is set we can add a storyboard. So, with the Item Template selected we can click on the ‘+’ to create a new storyboard:

newstoryboard

The content of the storyboard is not relevant, so I just animated the scale of the TextBlock.

Now, with the TextBlock selected, navigate to the Assets panel and click on Behaviors. Then, click and drag a ControlStoryBoardAction onto the TextBlock. examine it’s properties in the properties panel.

csbaprops 

 

So, if we just had a trigger that would fire when the data in the collection changes then it looks like we might be able to wire all of this up here in Blend. One problem is that we don’t have one, but File > New Item > Trigger will at least get us started.

The expression blend samples found here http://expressionblend.codeplex.com/ have some examples of custom triggers which are pretty similar. So, I looked at the DataEventTrigger sample and simplified it to produce a DataChangedTrigger. The original sample creates a new Dependency Property which it binds to the property you want to detect the changes on. This allows it to register a callback when the property changes and I simply invoke any attached Actions when this callback is called. So after modifying and building the code I can click on the ‘New’ button (next to the TriggerType) and select my new DataChangedTrigger. In it’s properties I can type the name of the binding, in this case ‘Name’ (for the property to which we wish to detect the changes). Now I select the Storyboard I made earlier.

To test it works I then wired up a click event handler on a button on the user interface which would make changes to the underlying data, like so.

 

        private void Button_Click(object sender, RoutedEventArgs e) 
        {
            ViewModel model = LayoutRoot.DataContext as ViewModel;
            if (model != null)
            {
                int index = counter++ % model.Data.Count;
                model.Data[index].Name = "Data has changed" + counter;
            }
        }

And indeed, the storyboard was triggered each time my data changed.

 

 

Technorati Tags: ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

 

Windows Live Tags: Wp7Dev,List,Item,Animation,data,MVVM,Beta,response,requirement,solution,separation,Anyway,Application,template,System,ObjectModel,ComponentModel,ListBoxDataChange,ViewModel,ObservableCollection,MyItem,Name,event,PropertyChangedEventHandler,PropertyChangedEventArgs,_name,custom,implements,mechanism,collection,additions,Expression,Blend,ContentGrid,Auto,Size,Fill,container,objects,PhoneApplicationPage,panel,DataContext,pane,Click,Create,Select,option,Object,View,Model,ListBox,ItemSource,Timelines,Bind,ItemsSource,result,ListBoxItem,items,mode,TextBlock,Grid,Text,Assets,Behaviors,ControlStoryBoardAction,wire,File,Trigger,DataEventTrigger,DataChangedTrigger,Dependency,Actions,code,TriggerType,Storyboard,handler,user,interface,Button_Click,sender,RoutedEventArgs,LayoutRoot,index,Count,Collections,purposes,deletions,templates,examples,vdfskjavnfhvnfhn,propertyName,dialog

5 thoughts on “WP7 ListBoxItem Animation on bound data changes (MVVM)

  1. I’m pretty new to Blend and Windows Phone. I tried to reproduce this, but I’m stuck at changing that trigger. Could you maybe explain this a little bit in more detail, please?

  2. Hi,
    I was wondering what needs to happen to make this work with current win phone sdk. You mentioned a ctp version that is does work with. Any idea on how I can accomplish, well, need to start an animation on alist box item (or not) based on teh contents of the data for each list item.
    Thanks for any suggestions you might have.
    Regards,
    Mike

Leave a Reply

Your email address will not be published. Required fields are marked *