Thursday, March 08, 2012

Set start page dynamically in wp7

My new Windows Phone 7 app "WeekCal" provides different calendar views. There are actually two week views and a month view. In the settings, the user can set the view to start with.
To start the app with the view the user has choosen, there are two steps necessary.

First step is to select the right view in the App.xaml.cs file and set it as root frame element. Do this in the Application_Launching method. Here is an example:

 private void Application_Launching(object sender, LaunchingEventArgs e)
 {
   Uri uri;
   // Read the user settings
   var views = IsolatedStorageProvider.LoadViewSettings();
   // Month view
   if (views.ShowMonthView)
   {
     uri = new Uri("/MonthViewPage.xaml", UriKind.Relative);
   }
   else
   {
     // Week view
     if (views.ShowFiveDaysWeek)
     {
       uri = new Uri("/FiveDaysPage.xaml", UriKind.Relative);
     }
     else
     {
       uri = new Uri("/SevenDaysPage.xaml", UriKind.Relative);
     }
   }
   // Set the start page
   ((App)Application.Current).RootFrame.Navigate(uri);
 }  

Everything looks ok, but the app does not start with the right view. There is one more easy step to do. In the WMAppManifest.xml file, the DefaultTask tag needs an empty NavigationPage property. So just delete the default entry MainPage.xaml.

<Tasks>
 <!--This is set in code (App.xaml.cs)-->
 <DefaultTask Name="_default" NavigationPage="" />

That's it, hope that helps.

Thursday, February 16, 2012

WP7 PhoneGap Appointments PlugIn

I'm involved in a project that needs a web frontend and clients for iPhone, Androide and of course WindowsPhone. Using a framework like PhoneGap is definitly a good point to start. In a later post I will write about how this all ended up. In this post I will focus on one of my first tasks, building a PhoneGap PlugIn that reads the calendar appointments.

I started with a web research and found a helpfull blog from Jesse here, explaining how to build a PlugIn. What I was missing in his blog, was how to return a value from the plugin. And there where some additional stumbling blocks to build this first PlugIn.

So lets start. First I created a new PhoneGap project in Visual Studio with the standard template (Visual Studio and PhoneGap is already installed).

In the next step I build the PlugIn class written in C#. The PlugIn should read my appointments and return them to the html page. So I added a new class AppointmentProvider in the Plugins folder of my project. This class must be
- in the namespace WP7GapClassLib.PhoneGap.Commands
- inherit from the BaseCommand base class

The rest of the code is more or less normal WP7 C# code.

 using System;
 using System.Collections.Generic;
 using System.Runtime.Serialization;
 using Microsoft.Phone.UserData;
 
 // The namespace must point to the phonegap commands  
 namespace WP7GapClassLib.PhoneGap.Commands 
 {
   // Use the phonegap base class
   public class AppointmentProvider : BaseCommand 
   {
     // Start appointment search with parameters
     public void Get(string args)
     {
       // Check the availability of the args
       if (string.IsNullOrWhiteSpace(args))
         throw new ArgumentNullException();
  
       // Deserialize the args to the ListArgs class
       var arguments = JSON.JsonHelper.Deserialize<ListArgs>(args);
       var appts = new Appointments();
       appts.SearchCompleted += 
         new EventHandler<AppointmentsSearchEventArgs>(appointments_SearchCompleted);
  
       // Set the parameters
       var start = DateTime.Now;
       var end = start.AddDays(int.Parse(arguments.Days));
       var max = int.Parse(arguments.Number);
  
       // Start the asynchronous search
       appts.SearchAsync(start, end, max, null);
     }
  
     // Get the result and pass it to the javascript code
     private void appointments_SearchCompleted(object sender, AppointmentsSearchEventArgs e)
     {
       // Build the result
       var apmts = new List<AptmResult>();
       foreach (var item in e.Results)
       {
         apmts.Add(new AptmResult() { 
           Text = string.Format("{0} {1}-{2}<br>{3}", 
           item.StartTime.ToShortDateString(), 
           item.StartTime.ToShortTimeString(), 
           item.EndTime.ToShortTimeString(),
           string.IsNullOrWhiteSpace(item.Subject) ? "Privat" : item.Subject)
         });
       }
  
       var result = new PluginResult(PluginResult.Status.OK, apmts);
  
       // Return the result
       DispatchCommandResult(result);
     }
  
     // Argument class to be serialized
     public class ListArgs
     {
       [DataMember]
       public string Number; 
  
       [DataMember]
       public string Days;
     }
  
     // Argument class to be serialized
     public class AptmResult
     {
       [DataMember]
       public string Text;
     }
   }
 }

Wow, my first PhoneGap PlugIn is finished :)

Now I need some html to show the results and some JavaScript code to call the PlugIn. The html is easy, just a button and a list.

<div>
  <button onclick="getAppointments();">
    Get Appointments</button>
</div>
<div>
  <ul id="apmtResults">
  </ul>
</div>

Then I need the JavaScript code to call the PlugIn. I did all the explanation in the code, so you can hopefully follow on what I did. One point I did not get is why a reference to a local jQuery lib does not work... and now when I'm writing this, I realize that I should have made an entry in the GapSourceDictionary.xml file. So this problem is solved as well.

<!-- Local reference to the jQuery lib does not work!? -->
<script type="text/javascript" charset="utf-8" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
  
<script type="text/javascript" charset="utf-8" src="phonegap-1.4.1.js"></script>
  
<script type="text/javascript">
  
  // Wait for PhoneGap to connect with the device
  document.addEventListener("deviceready", onDeviceReady, false);
 
  // PhoneGap is ready to be used!
  function onDeviceReady() {
    // Nothing to do for me
  }
  
  // APPOINTMENT PLUGIN
 
  // Call the AppointmentProvider plugin
  function getAppointments() {
    window.plugins.apmtProvider.showApmtProvider("10", "30");
  }
  
  // Create the class
  function ApmtProvider() {
    this.resultCallback = null;
  }
  
  ApmtProvider.prototype.showApmtProvider = function (number, days) {
    var args = {};
    if (number)
      args.Number = number;
  
    if (days)
       args.Days = days;
    PhoneGap.exec(onApmtOk, onApmtError, "AppointmentProvider", "Get", args);
  }
  
  // Create an instance of the class
  PhoneGap.addConstructor(function () {
    if (!window.plugins) {
      window.plugins = {};
    }
    window.plugins.apmtProvider = new ApmtProvider();
  });
  
  // Callback to handle the ok event
  function onApmtOk(result) {
    // Use jQuery to add the result to the div
    var apmts = [];
    // Clear the list first
    $("#apmtResults").empty();
    // Prepare the result by building an array
    $.each(result, function (i, item) {
      apmts.push('<li>' + item.Text + '</li>');
    });
    // Add the result array
    $("#apmtResults").append(apmts.join(''));
  }
  
  // Callback to handle the error event
  function onApmtError(result) {
    navigator.notification.alert(result, null, "Error");
  }
</script>  


We are done, but when I start the project it fails. First I need to know that I can't use the emulator, because there are no appointments. I need to run the project on a real device.

Still not working. It took me a while till I figured out, that in the WMAppManifest.xml file the entry
<Capability Name="ID_CAP_APPOINTMENTS"/>
was missing. After adding this everthing worked fine!

Please note that this is just a sample and error handling is missing ;-)

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.