0 Comments

I’ve been struggling with making semantic zoom work on a Windows 8 app that I been working on. I’m using it as an excuse to learn the MVVM pattern, but I ran into some some serious challenges making the two place nicely.

If you Google for the problem, the main post that will inevitably come back will be this post by Mikael Koskinen. It all hinges on the following code:

var collectionGroups = groupedItemsViewSource.View.CollectionGroups;
((ListViewBase)this.Zoom.ZoomedOutView).ItemsSource = collectionGroups;

While this approach will work, it doesn’t feel like a proper MVVM implementation for two reasons.

  1. You end up with code in the code behind for the view, which from my understanding violates the MVVM pattern.
  2. If you load the bound data asynchronously, which is pretty much required to get the app approved for the Windows Store, your bound collection will likely be empty when the code above runs, so when the data finishes loading, you semantic zoom source will still be empty.
  3. When you start getting into loading your views dynamically, this way gets really difficult to implement. I’m sure there’s a way to do it, but I couldn’t figure it out.

I found a better option when digging into the MVVM Light framework, specifically one of the samples they display. It seems that the Semantic Zoom control is pretty picky and needs its data source to be of type IGrouping. If you structure your data anything like me, you’re probably in the habit of binding to ObservableCollections most or all of the time, which do not implement IGrouping. So you have to implement the POCO classes/models in a way that IGrouping can understand, and then run them through a converter in your view binding. I would recommend looking at the example on the MVVM codeplex page under the section heading “The source code and the slides…” which links to a public SkyDrive folder with a zip file. If this disappears, the relevant converter code is below.

public class CvsToCollectionGroupsConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, string language)
	{
		var cvs = value as ICollectionView;
		if (cvs != null)
		{
			return cvs.CollectionGroups;
		}
		return null;
	}

	public object ConvertBack(object value, Type targetType, object parameter, string language)
	{
		throw new NotImplementedException();
	}
}