Colin Eberhardt's Technology Adventures

Capturing Windows Phone 7 Panorama Images

July 21st, 2011

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 (as described in this blog post), 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.

Implementing a Windows Phone 7 Conversation View

July 15th, 2011

This blog post looks at how to to create a conversation view, mimicking the SMS messaging interface within Windows Phone 7. This post shows how we can select different DataTemplate for each item in an ItemsControl to achieve this effect.

The Windows Phone 7 SMS messaging interface graphically illustrates between yourself and the other party by showing your messages as speech bubbles. Creating this view in your own application is not a straightforward process, with the template for each message being dependant on which side of the conversation it belongs to. In this blog post I will show how to create a simple template selector to achieve the view shown below:

Firstly, we’ll start with the model that represents our conversation:

public enum MessageSide
{
  Me,
  You
}
 
/// <summary>
/// An SMS message
/// </summary>
public class Message
{ 
  public string Text { get; set; }
 
  public DateTime Timestamp { get; set; }
 
  public MessageSide Side { get; set; }
}
 
/// <summary>
/// A collection of messages
/// </summary>
public class MessageCollection : ObservableCollection<Message>
{
}

It’s pretty simple, a collection of Message objects, each one with a Text and Timestamp property and a Side which indicates which side of the conversation it is on.

To aid in the design of this view, I created some design-time data:

<MessageCollection xmlns="clr-namespace:WP7ConversationView">
  <Message Text="Hi, How are you?" Side="You"/>
  <Message Text="Pretty good thanks. How are things at your end?" Side="Me"/>
  <Message Text="The weather here is a bit grim" Side="Me"/>
  <Message Text="Aye, not bad here, a bit of sun" Side="You"/>
  <Message Text="Do you want to hear a joke?" Side="You"/>
  <Message Text="Go on then" Side="Me"/>
  <Message Text="Knock knock" Side="You"/>
  <Message Text="Who's there?" Side="Me"/>
  <Message Text="Doctor!" Side="You"/>
  <Message Text="Doctor who?" Side="Me"/>
  <Message Text="Doctor Who ... ha ha ROFLMAO" Side="You"/>
  <Message Text="genius. Pure genius" Side="Me"/>
</MessageCollection>

Formatting a Message instance so that it looks like a speech bubble is pretty straightforward. We create a grid with three rows, the first has the message text, the next has the date and the final one contains a path which renders a small triangle:

<contribConverters:StringFormatConverter x:Key="StringFormatConverter"/>
 
 
<Grid Margin="30, 10, 5, 0"
      contribControls:GridUtils.RowDefinitions=",,"
      Width="420">
  <Rectangle Fill="{StaticResource PhoneAccentBrush}"
             Grid.RowSpan="2"/>
  <TextBlock Text="{Binding Path=Text}"
             Style="{StaticResource TextBlockStyle}"/>
  <TextBlock Text="{Binding Path=Timestamp, Converter={StaticResource StringFormatConverter},
                                            ConverterParameter='ddd, HH:mm'}"
             Style="{StaticResource TimestampStyle}"
             Grid.Row="1"/>
  <Path Data="m 0,0 l 16,0 l 0,16 l -16,-16"
        Fill="{StaticResource PhoneAccentBrush}"
        Margin="0,0,5,0"
        HorizontalAlignment="Right"
        Grid.Row="2"/>
</Grid>

The above code uses a couple of useful little classes from the WP7Contrib project. The first is GridUtils, which gives a simplified grid syntax. In the above example three rows were constructed using the simple string ",,". The second is a StringFormatConverter, which formats bound values using the supplied string.

Now, the problem is, the above markup renders a speech bubble for messages that have been sent, for received messages we want the bubble to have different margins (aligning it to the left) and have the triangle at the top. This could be achieved using a horrible mess of value converters, however, it would be better if we could just use different markup for each.

WPF and Silverlight v5 have the concept of implicit DataTemplates, where a template is selected based on the type of the item being rendered, however, this feature is not present in WP7. Also, in this case we need to select a template based on the Message.Side property.

The Silverlight ContentControl renders the object assign to its Content property using the DataTemplate specified by the ContentTemplate property. It is actually very easy to extend this control to additional DataTemplate properties which are applied based on the bound data:

public class MessageContentPresenter : ContentControl
{
  /// <summary>
  /// The DataTemplate to use when Message.Side == Side.Me
  /// </summary>
  public DataTemplate MeTemplate { get; set; }
 
  /// <summary>
  /// The DataTemplate to use when Message.Side == Side.You
  /// </summary>
  public DataTemplate YouTemplate { get; set; }
 
  protected override void OnContentChanged(object oldContent, object newContent)
  {
    base.OnContentChanged(oldContent, newContent);
 
    // apply the required template
    Message message = newContent as Message;
    if (message.Side == MessageSide.Me)
    {
      ContentTemplate = MeTemplate;
    }
    else
    {
      ContentTemplate = YouTemplate;
    }
  }
}

Using the above template-selector, we can supply different templates for the messages from either side of the conversation as follows:

<ItemsControl ItemsSource="{Binding}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <local:MessageContentPresenter Content="{Binding}">
        <local:MessageContentPresenter.MeTemplate>
          <!-- template for sent messages -->
          <DataTemplate>
            <Grid Margin="30, 10, 5, 0"
                  contribControls:GridUtils.RowDefinitions=",,"
                  Width="420">
              <Rectangle Fill="{StaticResource PhoneAccentBrush}"
                          Grid.RowSpan="2"/>
              <TextBlock Text="{Binding Path=Text}"
                          Style="{StaticResource TextBlockStyle}"/>
              <TextBlock Text="{Binding Path=Timestamp, Converter={StaticResource StringFormatConverter},
                                                        ConverterParameter='ddd, HH:mm'}"
                          Style="{StaticResource TimestampStyle}"
                          Grid.Row="1"/>
              <Path Data="m 0,0 l 16,0 l 0,16 l -16,-16"
                    Fill="{StaticResource PhoneAccentBrush}"
                    Margin="0,0,5,0"
                    HorizontalAlignment="Right"
                    Grid.Row="2"/>
            </Grid>                
          </DataTemplate>
        </local:MessageContentPresenter.MeTemplate>
        <local:MessageContentPresenter.YouTemplate>
          <!-- template for received messages -->
          <DataTemplate>
            <Grid Margin="5, 10, 30, 0"
                  contribControls:GridUtils.RowDefinitions=",,"
                  Width="420">
              <Path Data="m 0,0 l 0,16 l 16,0 l -16,-16"
                    Fill="{StaticResource PhoneAccentBrush}"
                    Margin="5,0,0,0"
                    HorizontalAlignment="Left"/>
              <Rectangle Fill="{StaticResource PhoneAccentBrush}"
                          Grid.Row="1" Grid.RowSpan="2"/>
              <TextBlock Text="{Binding Path=Text}"
                          Style="{StaticResource TextBlockStyle}"
                          Grid.Row="1"/>
              <TextBlock Text="{Binding Path=Timestamp, Converter={StaticResource StringFormatConverter},
                                                        ConverterParameter='ddd, HH:mm'}"
                          Style="{StaticResource TimestampStyle}"
                          Grid.Row="2"/>
            </Grid>
          </DataTemplate>
        </local:MessageContentPresenter.YouTemplate>
      </local:MessageContentPresenter>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

In the above XAML, the two templates are subtly different, rendering a triangle at a different grid location with a different orientation. The type of differences that would be hard to achieve via value converters.

I was able to test all of the above using design time data in Visual Studio, not having to execute the code once within the emulator!

You can download the full sourcecode for the project: WP7ConversationView.zip

You can also read part two of this blog post, where I show how to implement the text input giving a fully functional chat client.

Regards, Colin E.

From Silverlight to HTML5 CodeProject Article

July 1st, 2011

I have just published an article on codeproject which describes the differences in development between Silverlight and HTML5. In order to illustrate the differences, I have re-implemented my Windows Phone 7 JumpList control using HTML5/CSS3. You can see this control in action below:

(NOTE: this will look best in a browser that has support for CSS3 transitions and transformations. Try Google Chrome for example).

The control also works very well on mobile phones with webkit browsers, for example iPhone and Android. See the following video for a demonstration:

Regards, Colin E.

Silverlight UK User Group Presentation on Cross Platform XAML

July 1st, 2011

Earlier this week I presented a talk to the Silverlight UK User Group on cross-platform application development with WPF, Silverlight and Windows Phone 7.

Thanks to Gergley Orosz for these pictures

Thanks to all who attended my talk. Here is a copy of the presentation I gave:

You can see the Windows Phone 7 XAML Finance application which I demonstrated in the following video:

The Silverlight version is also available online.

Finally, the sourcecode for the simple cross-platform Twitter application I demonstrated is available to download: CrossPlatformDemo.zip

Regards, Colin E.