Montag, 6. Juni 2011

Windows Phone: Live tile changes with Mango

One of the unique features of the platform Windows Phone is the existence of a start page with tiles. Some of the tiles could be “live tiles” with some informations about the state of the Windows Phone app. For instance, a social network app could use the live tile to displays the number of unseen posts. The developer of a Windows Phone app can in the current version change the background image (local or remote image), title of the app and a counter (range 0-99) via Push Notifications.

Changes in Mango

With the new Mango release of Windows Phone we have the following changes:

  • Possible creation of secondary tiles with deep linking to a URI within your application (with parameters)
  • Access to your tiles with usage of ShellTile.ActiveTiles
  • Manipulate the tile appearance directly without push notifications with the StandardTileData class
  • Each tile now have a back page, which could have a image (remote or local), a separate title and a content text
  • In combination with the new background agent you can now schedule local tile updates
  • With the ShellTileSchedule class you can define that your tile image should be updated in the specified interval

Secondary live tiles

Adding a secondary tile to the start screen is done with the help of the ShellTile class. The StandardTileData class is used by tile creation or the tile update to hold the tile data.

// Check the existence of the new pinned tile
var shellTile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("PartnerID=4711"));
 
if (shellTile != null)
{
    return;
}
// Create the tile data with some proeprties
var tileData = new StandardTileData
    {
        BackgroundImage = new Uri("Red.jpg", UriKind.Relative),
        Title = "Secondary Tile",
        Count = 12,
        BackTitle = "Back page",
        BackContent = "This is the back page",
        BackBackgroundImage = new Uri("Blue.jpg", UriKind.Relative)
    };
 
// create the tile at the start screen
ShellTile.Create(new Uri("/MainPage.xaml?PartnerID=4711", UriKind.Relative), tileData);

The pictures used for the live tile must be in PNG or JPG format and have the size of 173x173 pixels PNG or JPG. The properties of the files in the solution have to be set to Content and “copy if newer”. The BackgroundImage or BackBackgroundImage could also be web URLs, than the UriKind Absolute should be used.

Deep Linking

If you use a secondary tile with a other Uri than “/” the NavigationService automatically navigates to the given page. In the NavigatedTo-Event you can access via QueryString to the parameters of the tile (in this case “PartnerID=4711”).

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
 
    if (NavigationContext.QueryString.ContainsKey("PartnerId"))
    {
        // do something here
    }
}

Updates the tiles

To access a tile the ActiveTiles prooperty of the ShellTile class will be used.

To find the primary tile and update the back content property:
var tileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString() == "/");
if (tileToFind != null)
{
    var newTileData = new StandardTileData
        {
            BackContent = "Changes for the back"
        };
 
    tileToFind.Update(newTileData);
}
To find a secondary tile and make some updates:

Find a tile with a specific parameter or all with a deep link to a page (for example details.xaml"):

// Find the tile we want to update.
var tileToFind = ShellTile.ActiveTiles.FirstOrDefault
    (x => x.NavigationUri.ToString().Contains("PartnerID=4711"));
 
// If tile was found, then update the Title
if (tileToFind != null)
{
    var newTileData = new StandardTileData
    {
        Title = textBoxTitle.Text
    };
 
    tileToFind.Update(newTileData);
}

Delete a pinned tile

Delete a secondary or main tile:

var tileFound = ShellTile.ActiveTiles
    .FirstOrDefault(tile => tile.NavigationUri.ToString().Contains("Details"));
 
if (tileFound != null)
{
    tileFound.Delete();
}

 

Background worker

With the new concept of Mango to run scheduled background agents you can easily create a local update service for tiles.

Start the background agent with help of the ScheduledActionService class. In my sample I use a PeriodicTask which will be scheduled every 30 minutes to check for updates of the live tiles. More details about scheduled tasks you can find here: Scheduled Tasks Overview for Windows Phone

(1) Add a new SchedulerAgent project to your solution

(2) Start a new PeriodicTask by using the ScheduledActionService

private static void StartPeriodicAgent()
{
    var periodicTask = new PeriodicTask("PeriodicAgent")
        {
            // The description is required. This is the string that the user
            // will see in the background services Settings page on the device.
            Description = "Periodic task for updating the tile"
        };
 
 
    // If the agent is already registered with the system,
    // call the StopPeriodicAgent helper method.
    if (ScheduledActionService.Find(periodicTask.Name) != null)
    {
        StopPeriodicAgent();
    }
 
    ScheduledActionService.Add(periodicTask);
}
 
private static void StopPeriodicAgent()
{
    ScheduledActionService.Remove("PeriodicAgent");
}

(3) At the Invoke Event of the ScheduledTaskAgent update the tile:

           if (task is PeriodicTask)
           {
               var tileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString() == "/");
               if (tileToFind != null)
               {
                   var rand=new Random(0);
 
                   var newTileData = new StandardTileData 
                   { 
                       Count = rand.Next(0,99)
                   };
                   Debug.WriteLine("Updated " + DateTime.Now.ToShortTimeString());
                   tileToFind.Update(newTileData);
               }
           }

The user have in Windows phone settings page a list of the current running background agents and can stop them.

image

Windows Phone: ListBox and ScrollViewer performance improvment

Read some intressting facts from the Windows Phone Performance team blog about performance improvments for the ListBox and ScrollViewer with Mango for Windows Phone: Listbox – ScrollViewer performance improvement for Mango and how it impacts your existing application? - Silverlight for Windows Phone Performance team blog - Site Home - MSDN Blogs

Sonntag, 5. Juni 2011

Windows Phone: Mango ProgressIndicator in SystemTray

With Mango for Windows Phone we have some cool changes for the system tray control. SystemTray is a DependencyObject and because Mango is now running on SL4, DependencyObject can be data bound. The other feature for the System Tray coming with Mango is a ProgressIndicator which can used to display progress on async operations.

More details and sample code about the new ProgressIndicator in the System Tray you can find here: System Tray Progress Indicator - Jaime Rodriguez - Site Home - MSDN Blogs

Data Binding of ProgressIndicator

Sample for programmatically data binding of the ProgressIndicator in the Loaded-Event of the page:

this.Loaded += ( x, y ) => 
    {
        var progressIndicator = SystemTray.ProgressIndicator;
        if (progressIndicator != null)
        {
            return;
        }
        progressIndicator = new ProgressIndicator();
                        
        SystemTray.SetProgressIndicator(this, progressIndicator);
                         
        var binding = new Binding("IsChecked") { Source = this.ProgressIndicatorIsVisible };
        BindingOperations.SetBinding(progressIndicator, ProgressIndicator.IsVisibleProperty, binding); 
                        
        binding = new Binding("IsChecked") { Source = this.ProgressIndicatorIsIndeterminate };
        BindingOperations.SetBinding(progressIndicator, ProgressIndicator.IsIndeterminateProperty, binding); 
                        
        binding = new Binding("Text") { Source = this.ProgressIndicatorText };
        BindingOperations.SetBinding(progressIndicator, ProgressIndicator.TextProperty, binding);
 
        binding = new Binding("Value") { Source = this.ProgressIndicatorValueSlider };
        BindingOperations.SetBinding(progressIndicator, ProgressIndicator.ValueProperty, binding);
    } ;              

Updated: Data Binding over declaration

Many thanks to a colleague, who give me the tip that data binding for SystemTray is also possible with XAML declaration.

See sample here: http://danielvaughan.org/post/Binding-the-WP7-ProgressIndicator-in-XAML.aspx

Samstag, 4. Juni 2011

Windows Phone: Mango tombstoning news

 

Mango_ExecutingModel

With the Mango update of Windows Phone to Windows Phone 7 many things will be changed. One of them is a better support for mutitasking behaviour of your application. This package consists of new background tasks (a will report about this later) and a new state in the executing model of Windows Phone.

The new state

Until Mango, an Windows Phone app will be tombstoned (=process will be killed), if the user leaves the app with the start menu button and call another application. With Mango Microsoft now introduce a new state for an application: “Dormant”. If there is enough memory left, the Windows Phone OS let the process of your app in memory until a other application process will kick it out. The user can than use the new function “Press and hold the back button for two seconds” to open the Mango fast switch application screen to return to one the dorming apps.

Is it really new?

But this state for your application is not really new. Because today with Windows Phone application development for version 7.0, you have to consider this state also. If you have a long running deactivation process, for example because of serialization, it could be that the user returns quicker back to your application than the deactivation is finished. In that case you have to handle the same behaviour that your old process is still running.

How to handle the revival

To distinguish between a activating of your app after tombstoning or revival you can use the IsApplicationInstancePreserved property of the ActivatedEventArgs in the Activated event of your app in that way:

private void ApplicationActivated(object sender, ActivatedEventArgs e)
{
    if (e.IsApplicationInstancePreserved)
    {
        ApplicationDataStatus = "application instance preserved.";
        return;
    }
 
    if (PhoneApplicationService.Current.State.ContainsKey("ApplicationDataObject"))
    {
        ApplicationDataStatus = "data from preserved state.";
        ApplicationDataObject = PhoneApplicationService.Current.State["ApplicationDataObject"] as string;
    }
}

Because in the case of application revival after the dormant state the constructors of your pages are not run through, you can define a bool variable which indicates in the NavigatedTo-Event if your application was tombstoned or not. It is not a good idea to create a static class with a starting state, which is set during the activating event, because the NavigatedTo of your page could be called earlier than the activated event (in case of long running deactivation, for example).

So best practice is to define a private member IsNewPageInstance, which is set to true within the page constructor.


bool isNewPageInstance = false;
 
// Constructor
public MainPage()
{
    InitializeComponent();
 
    this.isNewPageInstance = true;
            
}

In the NavigatedTo-Event we know now, if we have to restore our page state:

private void ApplicationActivated(object sender, ActivatedEventArgs e)
{
    if (e.IsApplicationInstancePreserved)
    {
        ApplicationDataStatus = "application instance preserved.";
        return;
    }
 
    if (PhoneApplicationService.Current.State.ContainsKey("ApplicationDataObject"))
    {
        ApplicationDataStatus = "data from preserved state.";
        ApplicationDataObject = PhoneApplicationService.Current.State["ApplicationDataObject"] as string;
    }
}

Testing of Tombstoning with the emulator

Another greeat new feature of the new Mango Windows Phone tools is that you can force the debugger to produce a tombstoning behaviour. The achieve this, you have to check a checkbox in the project properties of the debugging area.

image 

Conclusion

With the new state “dormant” and the fast switching ability of the Windows Phone OS for the Mango version in combination with the new backgound tasks Microsoft give us the tools of trade to emulate a better multitasking appearance. This behaviour is now similiar to other mobile plattforms and still assure a good battery life time. For me, a great step in the right direction.

Donnerstag, 2. Juni 2011

Windows Phone: Mango Turn-By-Turn Navigation

One of the cool new features of the Windows Phone 7 Mango release (Windows Phone 7.5) is the posibility to use turn by turn imagenavigation with voice guidance. This feature of Windows Phone Mango is integrated in the standard Bing Maps application and can use by developers from their own Windows Phone apps by the BingMapsDirectionsTask.

In my sample I have added two GeoCoordinate objects. One with my home place and the other with my working place at SDX AG. With the common concept of Windows Phone for interaction by using a Launcher or Chooser task you can then create a BingMapsDirectionsTask instance, set the locations as LabeledMapLocation to the start respectively end property of the BingMapsDirectionsTask and call the Show()-Method.

After this your application put in the background and the standard Bing Maps application will be called. Now you can use the standard turn by turn navigation features with voice guidance.

Conclusion

The new BingMapsDirectionsTask and turn-by-turn navigation features of the Bing Maps application of Windows Phone is a very cool function which will enable developers to build a new kind of Windows Phone applications. Furthermore all applications like “Where is my next bank or letter box” now can give the user the ability to give navigating instructions to this place.

 

Sample Code

private readonly GeoCoordinate destination;
 
private readonly GeoCoordinate target;
 
// Constructor
public MainPage()
{
    InitializeComponent();
 
    destination = new GeoCoordinate(49.907903, 8.510628); // Home
    target = new GeoCoordinate(50.139836, 8.742199); // SDX AG
}
 
private void NavigateButtonClick(object sender, RoutedEventArgs e)
{
    var bingMapsDirectionsTask = new BingMapsDirectionsTask();
 
    var destinationLocation = new LabeledMapLocation(
        "Home",
        destination);
 
    var targetLocation = new LabeledMapLocation(
        "SDX AG",
        target);
 
 
    bingMapsDirectionsTask.Start = destinationLocation;
    bingMapsDirectionsTask.End = targetLocation;
    bingMapsDirectionsTask.Show();
}