Fire collection updated events from a special Silverlight ItemsControl

Fire collection updated events from a special Silverlight ItemsControl

For the last few months I’ve been working solely with the Model-View-ViewModel pattern developed by John Gossman of WPF fame (http://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx). This pattern simply makes life easier 🙂 The idea is that you create special ViewModels which map the model directly to the view… so anything the view needs to use it can get from it’s view model (the view never talks straight to the model).

The mapping is achieved using DataContext + Data Binding. You pass in the ViewModel as the DataContext and then bind to things as required. When changes are needed to the interface, you simply update the data source and binding does the rest. You can bind values from simple text and colors through to more advanced scenarios involving zoom levels and positioning.

Controls like the ItemsControl play a major role in making all this work together. ItemsControl is great because you can quickly and easily map a list of items to a template in XAML and then place them within a panel of your choice. You then bind the ItemsControl to an ObservableCollection meaning the interface will update when eve you change an item in the collection.

There is one issue with this however – what if you want to show an unload animation before the collection changes are committed. You could fire an event from the VM to the V but this requires extra work and breaks some of the M-V-VM encapsulation we worked so hard to achieve.

I wanted a simple solution where I could just update the ObservableCollection and let the system handle the rest.

So I came up with a prototype control called the EventItemsControl. Basically the EventItemsControl intercepts collection changes and allows its child controls to run animations before updating the base collection and causing a layout refresh.

Sample and demo

Live Demo

To use the demo, adjust the index to insert/remove/replace at and start pressing buttons 🙂 Note, there is no error handling, so invalid indexes will crash the app.

The click events cause very simple changes to the underlying collection – there are no direct calls to any animation methods etc.

public void Reset()
{
    randomise();
}

public void Remove(int index)
{
    listViewModels.RemoveAt(index);
}

public void Insert(int index)
{
    SomeListItemViewModel item = getRandomItem(index);
    listViewModels.Insert(index, item);
}

public void Replace(int index)
{
    SomeListItemViewModel item = getRandomItem(index);
    listViewModels[index] = item;
}

<Sample Code>

EventItemsControl Sample Code

</Sample Code>

Create the control hierarchy

The EventItemsControl is defined just like a normal ItemsControl.


    
	
	    
	
    
    
	
	    
	
    

The only difference here is that I am creating an instance of EventItemsControl.

Any control which I want to be informed when the collection is changing needs to implement the ICollectionEventControl interface.

public interface ICollectionEventControl
{
	void OnBeginUnload();
	event EventHandler ProcessingFinished;
}

When the collection is changed, the EventItemsControl scans all children for this interface and calls OnBeginUnload when found. This way I can nicely encapsulate any animations etc. in the View without needing to tie up the ViewModel or create unneeded coupling between my layers.

My example user control here (SomeListItem – shown in the EventItemsControl DataTemplate above) runs an animation on creation and one when the items is removed.

public partial class SomeListItem : UserControl, ICollectionEventControl
{
	public SomeListItem()
	{
	    InitializeComponent();
	    
	    SBShow.Begin();
	    
	}

	#region ICollectionEventControl Members

	public void OnBeginUnload()
	{
	    SBHide.Completed += new EventHandler(SBHide_Completed);
	    SBHide.Begin();
	}

	void SBHide_Completed(object sender, EventArgs e)
	{
	    SBHide.Completed -= new EventHandler(SBHide_Completed);
	    if (ProcessingFinished != null)
	    {
		ProcessingFinished(this, EventArgs.Empty);
	    }
	}

	public event EventHandler ProcessingFinished;

	#endregion
}

The control contains a Storyboard defined in XAML – this code merely serves to kick off the animations.

How it works

Without going in to too much detail, the EventItemsControl buffers the ItemsSource collection – then once it’s ready it changes the underlying collection. It hooks in to the INotifyCollectionChanged events on the ObservableCollection which is passed in – then figures out how to best update the underlying collection based on the change type etc. You can have a peek at the sample code yourself if you want to know more.

A quick look at the EventItemsControl

Issues

There is an issue where if you clear the collection and write new data to it the older items may still be animating out when the new items are added, meaning the on screen list will grow for a little bit… I’ll look in to fixing this *one day* 🙂

2 thoughts on “Fire collection updated events from a special Silverlight ItemsControl

Comments are closed.