Capturing Windows Phone 7 Panorama Images

The Windows Phone 7 Panorama control is widely used in applications and to many has come to symbolise the Metro Design Language. Search for panorama images and you will find numerous promo-shots of Windows Phone 7 applications which display the panoramic contents of the application hub, with an image of a phone above it illustrating how the panorama slides through your viewport.

The image below shows a promo-shot for XAMLFinance, an application I have been working on:

Creating images like the one above takes a bit of work, fortunately you can turn off the parallax scrolling effect of the control allowing for easier image stitching, however the process is still quite manual.

For a bit of fun I thought it would be nice to template the Panorama control itself in order to display the full panorama contents on the emulator within a single screen, so that I can create my promo-shot with a single screengrab.

Unfortunately, the Panorama control is a little too tightly coupled to its template to allow this kind of radical re-design, so the next best options is to create a new page that hosts my PanoramaItems within an ItemsControl and style that.

So, we'll start with a page which hosts our items within an ItemsControl:

<ItemsControl Style="{StaticResource ItemsControlStyle}">
    <ItemsControl.Background>
        <ImageBrush ImageSource="PanoramaBackground.jpg" Opacity="0.5"/>
    </ItemsControl.Background>

    <!--Panorama item one-->
    <controls:PanoramaItem Header="item1" Style="{StaticResource PanoramaItemStyle}">
        <Grid>
            ...
        </Grid>
    </controls:PanoramaItem>

    <!--Panorama item two-->
    <controls:PanoramaItem Header="item2"  Style="{StaticResource PanoramaItemStyle}">
        <Grid>
            ...
        </Grid>
    </controls:PanoramaItem>

    <!--Panorama item three-->
    <controls:PanoramaItem Header="item3"  Style="{StaticResource PanoramaItemStyle}">
        <Grid>
            ...
        </Grid>
    </controls:PanoramaItem>
</ItemsControl>

We need to shrink each of those PanoramaItem instances so that they all fit on a single screen. We can easily shrink them with a ScaleTransform, however Silverlight only supports render transforms, what we really need here is a transform that affects layout also (for a brief overview of the difference see this blog post). Fortunately the Silverlight Toolkit has a LayoutTransformer class which emulates this behaviour, and whilst it is not available in the WP7 release, copying the code across I found it to be entirely compatible with the Silverlight WP7 version.

We can shrink our PanoramaItems as follows:

<system:Double x:Key="Scale">0.5</system:Double>

<Style x:Key="PanoramaItemStyle" TargetType="controls:PanoramaItem">
  <Setter Property="Width" Value="260"/>
  <Setter Property="Height" Value="290"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="controls:PanoramaItem">
        <!-- apply a LayoutTransform to the content in order to scale it -->
        <local:LayoutTransformer>
          <local:LayoutTransformer.LayoutTransform>
            <ScaleTransform ScaleX="{StaticResource Scale}"
                                            ScaleY="{StaticResource Scale}"/>
          </local:LayoutTransformer.LayoutTransform>

          <!-- the original content of the template follows -->
          <Grid Background="{TemplateBinding Background}" Margin="12,0,0,0">
            <Grid.RowDefinitions>
              <RowDefinition Height="auto"/>
              <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <ContentControl x:Name="header"
                            ContentTemplate="{TemplateBinding HeaderTemplate}"
                            Content="{TemplateBinding Header}"
                            FontSize="{StaticResource PhoneFontSizeExtraExtraLarge}"
                            FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                            HorizontalAlignment="Left" Margin="10,-2,0,26">
              <ContentControl.RenderTransform>
                <TranslateTransform x:Name="headerTransform"/>
              </ContentControl.RenderTransform>
            </ContentControl>
            <ContentPresenter Content="{TemplateBinding Content}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              Margin="{TemplateBinding Padding}" Grid.Row="1"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
          </Grid>
        </local:LayoutTransformer>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Now we need to styles and template the ItemsControl so that it looks like a Panorama. This is done as follows:

<Style TargetType="ItemsControl" x:Key="ItemsControlStyle">
  <Setter Property="VerticalAlignment" Value="Center"/>
  <Setter Property="HorizontalAlignment" Value="Center"/>
  <Setter Property="ItemsPanel">
    <Setter.Value>
      <ItemsPanelTemplate>
        <tk:WrapPanel Orientation="Horizontal"/>
      </ItemsPanelTemplate>
    </Setter.Value>
  </Setter>
  <Setter Property="Template">
    <Setter.Value>
      <!-- create a template for the ItemControl that makes it look like a panorama-->
      <ControlTemplate>
        <!-- the grid which displays the  panorama background -->
        <Grid Background="{TemplateBinding Background}">
          <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
          </Grid.RowDefinitions>

          <!-- the title, scaled to match the PanoramatItems-->
          <local:LayoutTransformer>
            <local:LayoutTransformer.LayoutTransform>
              <ScaleTransform ScaleX="{StaticResource Scale}"
                              ScaleY="{StaticResource Scale}"/>
            </local:LayoutTransformer.LayoutTransform>
            <TextBlock Text="{TemplateBinding Tag}"
                       FontSize="180"
                       FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                       HorizontalAlignment="Left" Margin="10,-2,0,26"/>
          </local:LayoutTransformer>

          <!-- the rendered items -->
          <ItemsPresenter Grid.Row="1"/>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

The end result is that we can see the entire panorama contents in a screen-shot ready state:

The big advantage of this approach is that any changes to the application can be reflected in the promo-shot very swiftly. It is also quite good fun having a fully functioning version of your panoramic hub in miniature!

You can download the sourcecode here: PanoramaShot.zip

Regards, Colin E.

MORE BY COLIN

gifbot - Building a GitHub App

blog comments powered by Disqus