Friday, January 27, 2012

SupportedOrientations not working on emulator

We all know the SupportedOrientations property on a windows phone page. Just set the value to Portrait and your App will only support the portrait orientation mode. Set it to Landscape and your App will only support the landscape orientation mode. Set it to PortraitOrLandscape and both are supported. That easy!
By the way, the property Orientation tells the device in which orientation to start.

But, yesterday I set the property on my main page from PortraitOrLandscape to Portrait only. And suddenly also on my other pages, where the SupportedOrientations property was still set to PortraitOrLandscape, the landscape mode was not supported anymore in my emulator. Hmmm, what happend here?

After checking everything for my mistake and not finding any and after a long helpless google session I finaly found my mistake by accident.

I used the pc keyboard (by hitting PageUp). This disables the rotation in the emulator and that was my only problem.

I hope this helps.

Tuesday, January 24, 2012

ImageButton control for Windows Phone 7

To build an image button in WPF or Silverlight is a commen task. Also in Windows Phone 7 but here we have an additional problem, the themes. The basics I found in this nice article.

What's not handled there is the themes. In the dark theme everything works fine but in the light theme the button is invisible because of the color (white in white). Therefore two more images are needed.

First I added two more dependency properties to the ImageButton class. And then I extended the template to look like that.

     <ControlTemplate x:Key="ImageButtonControlTemplate" TargetType="local:ImageButton">
       <Grid>
         <VisualStateManager.VisualStateGroups>
           <VisualStateGroup x:Name="CommonStates">
             <VisualState x:Name="Normal"/>
             <VisualState x:Name="MouseOver"/>
             <VisualState x:Name="Pressed">
               <Storyboard>
                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="PressedImage">
                   <DiscreteObjectKeyFrame KeyTime="0">
                     <DiscreteObjectKeyFrame.Value>
                       <Visibility>Visible</Visibility>
                     </DiscreteObjectKeyFrame.Value>
                   </DiscreteObjectKeyFrame>
                 </ObjectAnimationUsingKeyFrames>
                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="NormalImage">
                   <DiscreteObjectKeyFrame KeyTime="0">
                     <DiscreteObjectKeyFrame.Value>
                       <Visibility>Collapsed</Visibility>
                     </DiscreteObjectKeyFrame.Value>
                   </DiscreteObjectKeyFrame>
                 </ObjectAnimationUsingKeyFrames>
                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="PressedLightThemeImage">
                   <DiscreteObjectKeyFrame KeyTime="0">
                     <DiscreteObjectKeyFrame.Value>
                       <Visibility>Visible</Visibility>
                     </DiscreteObjectKeyFrame.Value>
                   </DiscreteObjectKeyFrame>
                 </ObjectAnimationUsingKeyFrames>
                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="NormalLightThemeImage">
                   <DiscreteObjectKeyFrame KeyTime="0">
                     <DiscreteObjectKeyFrame.Value>
                       <Visibility>Collapsed</Visibility>
                     </DiscreteObjectKeyFrame.Value>
                   </DiscreteObjectKeyFrame>
                 </ObjectAnimationUsingKeyFrames>
               </Storyboard>
             </VisualState>
             <VisualState x:Name="Disabled"/>
           </VisualStateGroup>
         </VisualStateManager.VisualStateGroups>
         <StackPanel Visibility="{StaticResource PhoneDarkThemeVisibility}">
           <Image x:Name="NormalImage" Source="{TemplateBinding Image}"/>
           <Image x:Name="PressedImage" Source="{TemplateBinding PressedImage}" Visibility="Collapsed"/>
         </StackPanel>
         <StackPanel Visibility="{StaticResource PhoneLightThemeVisibility}">
           <Image x:Name="NormalLightThemeImage" Source="{TemplateBinding ImageLightTheme}"/>
           <Image x:Name="PressedLightThemeImage" Source="{TemplateBinding PressedImageLightTheme}" Visibility="Collapsed"/>
         </StackPanel>
       </Grid>
     </ControlTemplate>  

The magic are the two StackPanels and there visibility bound to the static resources PhoneDarkThemeVisibility and PhoneLightThemeVisibility which are part of the wp7 framework.

Hope that helps!

Monday, January 23, 2012

WP7 Shortcuts App Redesign

Last weekend I finished the redesign of my Shortcuts App. I'm happy that my poor design is gonne and that I can provide you now a real Windows Phone Look&Feel. Second good news is that I integrated webcams.travel api to search for webcams. Will be in Marketplace very soon!

Hope you will like it!





WP7 ShellTileSchedule, Secondary Tiles Update Problem

Already in one of my last posts I blogged about the ShellTileSchedule class. I love the simplicity of this class and still use it in my Shortcuts App you can get here. After a complete re-design of the App, I did a lot of testing and found an interesting bug.

My App did not refresh webcam images on phone, sms and mail live tiles.

I know the limitations of the ShellTileSchedule class and checked them again and again.

Limitation
Note that you can only provide a RemoteImageUri. Therefore you must provide an online and available URI that represents an image to download and display. You can’t reference URI from your local application. The image size can NOT exceed 80KB, and download time can NOT exceed 60 sec.

The bug must be somewhere else. Half a night later I found the problem in the link property of the tile to update. If the link has more than one parameter (like my phone, sms and mail tiles),  the update will not work. If there is non or only one parameter (like all my other functions) it works. So I changed the signature of the links and now everything is fine.

Old code
         var tile = new StandardTileData()
         {
           Title = "Demo"
         };
         var link = new Uri
           (string.Format(
             "/Phone.xaml?name={0}&number={1}", 
             nameTextBlock.Text, 
             numberTextBlock.Text)
             , UriKind.Relative);
         ShellTile.Create(link, tile);  

New code
         var tile = new StandardTileData()
         {
           Title = "Demo"
         };
         var link = new Uri
           (string.Format(
             "/Phone.xaml?name={0}%{1}", 
             nameTextBlock.Text, 
             numberTextBlock.Text)
             , UriKind.Relative);
         ShellTile.Create(link, tile);  

On the target form I changed the way to read the parameter to this
       if (this.NavigationContext.QueryString.Keys.Contains(Constants.NAME))
         _name = this.NavigationContext.QueryString[Constants.NAME];
       var x = _name.Split(new Char[] { '%' });
       _name = x[0];
       _number = x[1];  

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.


Friday, January 06, 2012

Tile Tasks, the shortcut app for Windows Phone

Shortcuts are often very helpfull. Specially on a slow device like my iPhone 3GS, it would be very helpful to have some shortcuts to make a call, send a sms or toggle airplane mode on and off instead to tap thru all this menues. If I hopefully get my Nokia Lumia 900 soon, I do not want to miss this functionality again and I built Tile Tasks. The functionality today includes:
- Quick dial your friends phone number
- Send a sms with a default text
- Send an email with default subject and body
- Navigate to an internet address
- Add a Webcam to your tile
- Shortcut Airplane mode
- Shortcut WiFi
- Shortcut Bluetooth
- Shortcut Cellular

And here what it looks like to build a shortcut for a call, an email and a website link with a webcam image as tile background that will be updated every hour.




The generated tiles have frontside and backside information and gives the user quick access to many functionalities. The example tiles looks like this.

 
Like always in my posts, what about the technical stuff? Was there anything exciting? Plenty of them. One of the intersting and new stuff was to show the webcam image on the tile and update them periodicaly.


There are different options to do that. I've chosen the simplest and used the ShellTileSchedule class. My methode looks as simple as this:
 
private static void createTileSchedule(ShellTile tile, Uri link)

{
  var tileSchedule = new ShellTileSchedule(tile)
  {
    Recurrence = UpdateRecurrence.Interval,
    Interval = UpdateInterval.EveryHour,
    StartTime = DateTime.Now,
    RemoteImageUri = link
  };
  tileSchedule.Start();
}

The parameter tile is the tile where you like to add the webcam image as background. It can be the primary or a secondary tile. The link is the absolut address to a webcam image. Supported are jpg and png files, note that you can only provide a RemoteImageUri. Therefore you must provide an online and available URI that represents an image to download and display. You can’t reference URI from your local application. The image size can NOT exceed 80KB, and download time can NOT exceed 60 sec.

Is there anything else to deal with? Oh yes! The ShellTileSchedule class is very easy to use but has some big disadvantages. For me the most importants are:

1. You can’t get the status of the ShellTileSchedule. Even worse is the fact that the schedule might be stopped because for whatever reason (ex. phone is on Airplane Mode) and the tile background wount be refreshed.
2. You have to wait at least 1 hour before the tile is updated for the first time.

Here is an excelent article from Mark Monster that addresses this points. And here are some good informations from Microsoft about Tile Notifications (Chapter 3).

And after all, if you think you will like the app and it is helpfull for you, please get it here.