Tuesday, January 10, 2012

Generic About Page for Windows Phone App

Now I have five Apps in the Marketplace. One thing every App needs is an About Page. If you give the customer an About Page it looks just more professional and the buttons to rate the App or navigate to your other Apps should give you as a developer an advantage.
Copy&Paste was my first approache. But we all now that's not a good one. A lot of work and a good chance to miss a customisatzion and wrong information or links are standing on the page.
To built a generic About Page is a much better idea. Here is what my page looks like.


Pretty simple, isn't it? And here is what the xaml code and the code behind looks like.

 <phone:PhoneApplicationPage 
   x:Class="AboutPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   FontFamily="{StaticResource PhoneFontFamilyNormal}"
   FontSize="{StaticResource PhoneFontSizeNormal}"
   Foreground="{StaticResource PhoneForegroundBrush}"
   SupportedOrientations="Portrait" Orientation="Portrait"
   mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
   shell:SystemTray.IsVisible="True">
 
   <!--LayoutRoot is the root grid where all page content is placed--> 
   <Grid x:Name="LayoutRoot" Background="Transparent">
     <Grid.RowDefinitions>
       <RowDefinition Height="Auto"/>
       <RowDefinition Height="*"/>
     </Grid.RowDefinitions>
  
     <!--TitlePanel contains the name of the application and page title-->
     <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
       <TextBlock x:Name="ApplicationTitle" Text="Demo" Style="{StaticResource PhoneTextNormalStyle}"/>
       <TextBlock x:Name="PageTitle" Text="{Binding Path=LocalizedResources.About, Source={StaticResource LocalizedStrings}}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
     </StackPanel>
  
     <!--ContentPanel - place additional content here-->
     <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
       <StackPanel Orientation="Vertical">
         <TextBlock x:Name="appNameTextBlock" TextAlignment="Center" Text="Demo" FontSize="{StaticResource PhoneFontSizeExtraLarge}" Foreground="{StaticResource PhoneAccentBrush}"/>
         <TextBlock x:Name="appAuthorTextBlock" TextAlignment="Center" Margin="0,0,0,20" Text="Demo" FontSize="{StaticResource PhoneFontSizeMediumLarge}"/>
         <TextBlock x:Name="appDescriptionTextBlock" Margin="0,0,0,30" TextWrapping="Wrap" Height="170" Text="{Binding Path=LocalizedResources.Description, Source={StaticResource LocalizedStrings}}" FontSize="{StaticResource PhoneFontSizeNormal}"/>
         <Button x:Name="buyButton" Click="buyButton_Click" Content="{Binding Path=LocalizedResources.BuyNow, Source={StaticResource LocalizedStrings}}" />
         <Button x:Name="rateButton" Click="rateButton_Click" Content="{Binding Path=LocalizedResources.RateNow, Source={StaticResource LocalizedStrings}}" />
         <Button x:Name="emailButton" Click="emailButton_Click" Content="{Binding Path=LocalizedResources.SendEmail, Source={StaticResource LocalizedStrings}}" />
         <Button x:Name="showAppsButton" Click="showAppsButton_Click" Content="{Binding Path=LocalizedResources.GetMoreApps, Source={StaticResource LocalizedStrings}}" />
  
       </StackPanel>
     </Grid>
   </Grid>
 </phone:PhoneApplicationPage>  

   public partial class AboutPage : PhoneApplicationPage
   {
     public AboutPage()
     {
       InitializeComponent();
  
       appNameTextBlock.Text = string.Format("{0} V{1}", ApplicationManifest.Title, ApplicationManifest.Version);
  
       appAuthorTextBlock.Text = ApplicationManifest.Publisher;
       // Use this if you do not have a translation in your rescources
       //appDescriptionTextBlock.Text = ApplicationManifest.Description;
       ApplicationTitle.Text = ApplicationManifest.Title;
       buyButton.IsEnabled = ApplicationLicense.IsTrial;
     }
  
     private void buyButton_Click(object sender, RoutedEventArgs e)
     {
       new MarketplaceDetailTask().Show();
     }
  
     private void rateButton_Click(object sender, RoutedEventArgs e)
     {
       new MarketplaceReviewTask().Show();
     }
  
     private void emailButton_Click(object sender, RoutedEventArgs e)
     {
       new EmailComposeTask()
       {
         To = "jctrachselatbluewin.com",
         Subject = "Question/comment to " + ApplicationManifest.Title,
       }.Show();
     }
  
     private void showAppsButton_Click(object sender, RoutedEventArgs e)
     {
       new MarketplaceSearchTask()
       {
         SearchTerms = "Jean-Claude Trachsel"
       }.Show();
     }
   }  

Because a lot of information is provided by the WMAppManifest.xml file anyway, we take it from there. Is this making sense? I hope so! To access the file I use the following class.

   public static class ApplicationManifest
   {
     private static Dictionary<string, string> _properties;
  
     private static Dictionary<string, string> Properties
     {
       get
       {
         if (null == _properties)
         {
           _properties = new Dictionary<string, string>();
           var appManifestXml = XDocument.Load("WMAppManifest.xml");
           using (var rdr = appManifestXml.CreateReader(ReaderOptions.None))
           {
             rdr.ReadToDescendant("App");
             if (!rdr.IsStartElement())
             {
               throw new System.FormatException("App tag not found in WMAppManifest.xml ");
             }
             rdr.MoveToFirstAttribute();
             while (rdr.MoveToNextAttribute())
             {
               _properties.Add(rdr.Name, rdr.Value);
             }
           }
         }
         return _properties;
       }
     }
  
     public static string Version
     {
       get { return Properties["Version"]; }
     }
  
     public static string ProductId
     {
       get { return Properties["ProductID"]; }
     }
  
     public static string Title
     {
       get { return Properties["Title"]; }
     }
  
     public static string TitleUc
     {
       get { return !string.IsNullOrEmpty(Title) ? Title.ToUpperInvariant() : null; }
     }
  
     public static string Genre
     {
       get { return Properties["Genre"]; }
     }
  
     public static string Description
     {
       get { return Properties["Description"]; }
     }
  
     public static string Publisher
     {
       get { return Properties["Publisher"]; }
     }
   }  

If you need localization like I do in all of my Apps, there is one more class needed to do the binding like you can see in the xaml code above. This class accesses a file named AppResources.resx.
   public class LocalizedStrings
   {
     private static AppResources localizedResources = new AppResources();
     public AppResources LocalizedResources { get { return localizedResources; } }
   }  

I hope this helps and fell free to use my About Page in your App.


No comments: