Semantic zoom – beyond the stock behaviour – #windev #winrt

Update: Download a sample implementation from skydrive

Last year I was working on Cineworld app for Windows 8. Having use LongListSelector I wanted to ensure that I can transfer the look and feel. However I was fighting multiple battles – the first one is that while LongListSelector is a single control, SemanticZoom control itself contains ZoomInView and ZoomedOutView. These are independent of each other and most examples then to show settings data to zoomed in view and then zoomed out view referring to zoomed in view. It gets messy – maybe I am just slow but it was not intuitive and took me ages.

While mucking around, I came across this post and from there, kept trying till I got what I wanted.


Screenshot (220)

Screenshot (221)


    <CollectionViewSource x:Name="cvsFilms" IsSourceGrouped="true" />

    <!-- Zoomed out templates and selectors -->
    <DataTemplate x:Key="GroupTemplate">
        <Border Margin="6" Width="75" Height="75" Background="Black" >
            <TextBlock Text="{Binding Name}" Foreground="White" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <DataTemplate x:Key="EmptyGroupTemplate">
        <Border Margin="6" Width="75" Height="75" BorderBrush="Black" BorderThickness="1" Background="LightGray">
            <TextBlock Text="{Binding Name}" Foreground="Black" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
            Empty="{StaticResource EmptyGroupTemplate}"
            Full="{StaticResource GroupTemplate}" />
    <DataTemplate x:Key="GroupHeaderTemplate">
        <Border Margin="20, 20, 0, 0" Width="75" Height="75" Background="Black" Tapped="Grid_Tapped">
            <TextBlock Text="{Binding Key}" Foreground="White" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
    <DataTemplate x:Key="GroupItemTemplate">
        <Border Margin="14, 20, 0, 0">
            <Grid Height="350" Width="185" Margin="0">
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                <Image  Source="{Binding PosterUrl}" Height="278" Width="185" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="UniformToFill"/>
                <TextBlock Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Title}" HorizontalAlignment="Center" FontFamily="Segoe UI" />

<SemanticZoom x:Name="semanticZoom" Background="White" HorizontalAlignment="Stretch" Grid.Row="1" VerticalAlignment="Stretch">
        <GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False" HorizontalAlignment="Center" ItemTemplateSelector="{StaticResource FilmGroupEmptyOrFullSelector}" >
                    <WrapGrid Background="LightGray" MaximumRowsOrColumns="9" VerticalChildrenAlignment="Center" Orientation="Horizontal"/>
        <GridView x:Name="gvZoomedInFilms" ItemsSource="{Binding Source={StaticResource cvsFilms}}" IsSwipeEnabled="True" ScrollViewer.IsHorizontalScrollChainingEnabled="False" ItemTemplate="{StaticResource GroupItemTemplate}">
                    <VirtualizingStackPanel Orientation="Horizontal"/>
                <GroupStyle HeaderTemplate="{StaticResource GroupHeaderTemplate}">
                            <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,50,0"/>
List<GroupInfoList<object>> dataLetter = null;

public ListFilms()

    FilmData cd = new FilmData(App.Films);

    dataLetter = cd.GroupsByLetter; 
    cvsFilms.Source = dataLetter;
    gvZoomedInFilms.SelectionChanged -= gvZoomedIn_SelectionChanged;
    gvZoomedInFilms.SelectedItem = null;
    (semanticZoom.ZoomedOutView as ListViewBase).ItemsSource = cd.FilmHeaders;     gvZoomedInFilms.SelectionChanged += gvZoomedIn_SelectionChanged;

    semanticZoom.ViewChangeStarted -= semanticZoom_ViewChangeStarted;
    semanticZoom.ViewChangeStarted += semanticZoom_ViewChangeStarted;

void semanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
    if (e.SourceItem == null)

    if (e.SourceItem.Item.GetType() == typeof(HeaderItem))
        HeaderItem hi = (HeaderItem)e.SourceItem.Item;

        var group = dataLetter.Find(d => ((char)d.Key) == hi.Name);
        if (group != null)
            e.DestinationItem = new SemanticZoomLocation() { Item = group };

// FilmData looks like this
private List<GroupInfoList<object>> groupsByLetter = null;

public List<GroupInfoList<object>> GroupsByLetter
        if (groupsByLetter == null)
            groupsByLetter = new List<GroupInfoList<object>>();

            var query = from item in Collection
                        orderby ((FilmInfo)item).Title
                        group item by ((FilmInfo)item).HeaderChar into g
                        select new { GroupName = g.Key, Items = g };

            foreach (var g in query)
                GroupInfoList<object> info = new GroupInfoList<object>();
                info.Key = g.GroupName;
                foreach (var item in g.Items)

        return groupsByLetter;

List<HeaderItem> filmHeaders = null;
public List<HeaderItem> FilmHeaders
        if (filmHeaders == null)
            filmHeaders = new List<HeaderItem>();

            char c = '#';

            filmHeaders.Add(new HeaderItem() { Name = '#', IsEnabled = this.GroupsByLetter.Exists(k => ((char)k.Key) == c) });
            for (int i = 65; i <= 90; i++)
                c = (char)i;

                if (this.GroupsByLetter.Exists(k => ((char)k.Key) == c))
                    filmHeaders.Add(new HeaderItem() { Name = c, IsEnabled = true });
                    filmHeaders.Add(new HeaderItem() { Name = c, IsEnabled = false });

        return filmHeaders;

I think that’s about it. Its been 6 months now and I think I am going to change how it looks 🙂 some stuff will remain as it is, others will have to change


One thought on “Semantic zoom – beyond the stock behaviour – #windev #winrt

  1. Pingback: Semantic zoom – beyond the stock behaviour

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s