In this development tutorial series we are going to cover another new feature given to developers with the “Mango” SDK; Background Agents. Background agents give the ability for an application to run code in the background. This can be a reminder, toast notifications, or file transfers just to name a few. These background agents come in two flavors, either a Period Agents or Resource Intensive Agents. A periodic agent runs for brief period of time at a regular interval. A resource intensive agent runs for longer periods of time in a one-time occurrence. For this tutorial we are going to use a periodic agent in this tutorial to demonstrate how to implement and code it. Jump below for a look at the code and see how to implement a periodic agent.

// WP7LDBStorage - Settings.xaml
// Grid where we bind checkbox
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<CheckBox Content="Live Tile Enabled" Height="72" HorizontalAlignment="Left" Margin="8,39,0,0" Name="cbLiveTile" VerticalAlignment="Top" Width="444" Click="cbLiveTile_Click"/>
<TextBlock HorizontalAlignment="Left" Margin="8,8,0,0" TextWrapping="Wrap" Text="Live Tile" VerticalAlignment="Top"/>
<Button Content="Create Channel" Margin="8,138,8,0" VerticalAlignment="Top" Name="btnCreateChannel" Click="btnCreateChannel_Click" />
<CheckBox Content="Enable Background Agent" Height="72" HorizontalAlignment="Left" Margin="12,249,0,0" Name="cbBackgroundAgent" VerticalAlignment="Top" Width="436" IsThreeState="False" Checked="cbBackgroundAgent_Checked" Unchecked="cbBackgroundAgent_Unchecked" IsChecked="{Binding IsEnabled}"/>
<TextBlock Height="30" HorizontalAlignment="Left" Margin="6,102,0,0" Name="textBlock1" Text="Toast Notifications" VerticalAlignment="Top" />
<TextBlock Height="30" HorizontalAlignment="Left" Margin="9,216,0,0" Name="textBlock2" Text="Background Agents" VerticalAlignment="Top" />
</Grid>

This code above is from the “Settings.xaml” where we implement a new checkbox for the background agent to be enabled and disabled. For this particular tutorial we are going to Bind the checkbox to the IsEnabled and run a method in our code file to check it. This will allow us to eliminate the need to open an isolated storage file and store and retrieve data from it.

// WP7LDBStorage - Settings.xaml.cs - SOF
#define DEBUG_AGENT
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.IO;
using System.IO.IsolatedStorage;
using Microsoft.Phone.Notification;
using System.Diagnostics;
// Directive for the data model
using WP7LDBStorage.Model;
using Microsoft.Phone.Tasks;
using Microsoft.Phone.Scheduler;
namespace WP7LDBStorage
{
public partial class Settings : PhoneApplicationPage
{

PeriodicTask periodicTask;
string periodicTaskName = "PeriodicAgent-BW";
public bool agentsAreEnabled = true;
public Settings()
{

Here we are looking at the import statements and by this tutorial you should know that we have, of course, added at least one. Here we are using “using Microsoft.Phone.Scheduler” which gives us access to Scheduler features utilized by Windows Phone such as alarms, reminders and what we are in it for; background agents. You will then see that just before our default constructor we create a PeriodicTask object called periodicTask that will not be instantiated yet, and a string object called periodicTaskName with the string of “PeriodicAgent-BW”. We also create a public bool object called agentsAreEnabled setting the variable to true. After this is done we can move onto the bottom of our code as it will take place after the code we made for our push notifications post previous.

// WP7LDBStorage - Settings.xaml.cs - EOF
private void StartPeriodicAgent()
{
// Variable for tracking enabled status of background agents for this app.
agentsAreEnabled = true;
// Obtain a reference to the period task, if one exists
periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
// If the task already exists and background agents are enabled for the
// application, you must remove the task and then add it again to update
// the schedule
if (periodicTask != null)
{
RemoveAgent(periodicTaskName);
}
periodicTask = new PeriodicTask(periodicTaskName);
// The description is required for periodic agents. This is the string that the user
// will see in the background services Settings page on the device.
periodicTask.Description = "This is a periodic background agent for Client Contact Information";
// Place the call to Add in a try block in case the user has disabled agents
try
{
ScheduledActionService.Add(periodicTask);
// If debugging is enabled, use LaunchForTest to launch the agent in one minute.
#if(DEBUG_AGENT)
ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60));
#endif
}
catch (InvalidOperationException exception)
{
if (exception.Message.Contains("BNS Error: The action is disabled"))
{
MessageBox.Show("Background agents for this application have been disabled by the user.");
agentsAreEnabled = false;
}
if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
{
// No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
}
cbBackgroundAgent.IsChecked = false;
}
catch (SchedulerServiceException)
{
// No user action required.
cbBackgroundAgent.IsChecked = false;
}
}

Above we have the method StartPeriodicAgent for which creating the periodic agent is responsible. The first thing we want to do is make sure that our object agentsAreEnabled is set to true, and we are also wanting to check and see if a periodic task exists with ScheduledActionService.Find(periodicTaskName) as PeriodicTask; which finds a given variable (if it exists) after defining what we are looking for (hence “as PeriodicTask”). If the task does exist then we want to remove it so we can add it again to keep the schedule up to date. After the if statement we are going to instantiate the object again. Next we need to give our task a description that will appear in the settings menu under the Background Tasks menu. This can be anything descriptive that the user can use to identify your application.

Next we have a try catch statement that is being utilized when we try and add our periodic task using ScheduledActionService which manages scheduled actions and the Add command registers the task with the operating system. After this we have an if statement that only applies if we are debugging the application. This command speeds up the amount of time in-between scheduled tasks from every 30 minutes to any time (in seconds) we want to make it. In the catch statement we check for an InvalidOperationException. With the result we may get we use exception.Message.Contains(“BNS Error: The action is disabled”) to determine if the user has disabled the ability for background agents to be used. If it has, we throw a message box onto the screen letting the user know this and disable or make agentsAreEnabled equal to false. If there are any other exceptions that are thrown we are going to turn agentsAreEnabled from true to false.

private void RemoveAgent(string name)
{
try
{
ScheduledActionService.Remove(name);
}
catch (Exception)
{
}
}
bool ignoreCheckBoxEvents = false;
private void cbBackgroundAgent_Checked(object sender, RoutedEventArgs e)
{
if (ignoreCheckBoxEvents)
return;
StartPeriodicAgent();
}
private void cbBackgroundAgent_Unchecked(object sender, RoutedEventArgs e)
{
if (ignoreCheckBoxEvents)
return;
RemoveAgent(periodicTaskName);
}

Here we have the method we used in the event above. It is used to remove the periodic task we have created. To do this we first need a try catch statement to block against any unwanted errors reaching the user. Within the try statement we want to use the Remove function that is part of the ScheduledActionService. Before the next method we are going to create a Boolean object called ignoreCheckBoxEvents and set it to false. In the next two methods we have click event handlers that run when the checkbox to enable or disable the background agents is checked or unchecked. If the checkbox is checked, we run an if statement to check and see if ignoreCheckBoxEvents is true and if it is then do nothing else run the method StartPeriodicAgent(). If we uncheck the checkbox we run an if statement to check and see if ignoreCheckBoxEvents is true and if it is then we want to return, else we run the RemoveAgent method to remove the periodic agent we have enabled.

// Use this method to bind to the checkbox and keep it checked or unchecked
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
ignoreCheckBoxEvents = true;
periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
if (periodicTask != null)
{
ContentPanel.DataContext = periodicTask;
}
ignoreCheckBoxEvents = false;
}
}
}

The method above is used in conjunction with the XAML file we reviewed before. We make ignoreCheckBoxEvents equal to true and look for the periodic task and instantiate it to our PeriodicTask object. If the periodic task is not empty we want to pass the notification to make it checked, else keep it unchecked. After the if statement we set ignoreCheckBoxEvents equal to false. Below we have the Windows Phone Scheduled Task Agent that we created up above. The only difference is that this is the complete code to get a push notification to appear for the user and to update the count on the tile.

// PeriodicTaskAgentBW - ScheduledAgent.cs
#define DEBUG_AGENT
using System.Windows;
using Microsoft.Phone.Scheduler;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Info;
using System.Linq;
using System;
namespace PeriodicTaskAgentBW
{
public class ScheduledAgent : ScheduledTaskAgent
{
private static volatile bool _classInitialized;
int count;
///
/// ScheduledAgent constructor, initializes the UnhandledException handler
///
public ScheduledAgent()
{

if (!_classInitialized)
{
_classInitialized = true;
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += ScheduledAgent_UnhandledException;
});
}
}
/// Code to execute on Unhandled Exceptions
private void ScheduledAgent_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}

Here we can see that we are importing more using statements in the code above. The main ones that we may or may not have corvered in the code previous are using Microsoft.Phone.Scheduler;, using Microsoft.Phone.Shell;, using Microsoft.Phone.Info; and using System.Linq;. We have covered what all of these do except for System.Linq. We are going to use Linq to update our live tile count and show that the background agent is in fact working. You will also see that we are defining a DEBUG_AGENT which we will use only in instances where we need to debug the application and speed up the time between when the scheduled agent runs. The code after the using statements is auto generated and is used to catch Unhandled Exceptions as well as the standard default constructor.

///

<summary> /// Agent that runs a scheduled task
/// </summary>

&nbsp;

////// The invoked task
/// ///
/// This method is called when a periodic or resource intensive task is invoked
///
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
string toastTitle = "";
if (task is PeriodicTask)
{
// Execute periodic task actions here.
toastTitle = "Update";
ShellTile appTile = ShellTile.ActiveTiles.First();
if (appTile != null)
{
var standardTile = new StandardTileData
{
//Title = "Contact Info",
//BackgroundImage = “”,
Count = 1 // any number can go here, leaving this null shows NO number
//BackTitle = “”,
//BackBackgroundImage = ””,
//BackContent = “”
};
appTile.Update(standardTile);
}
}
else
{
// Perform resource intensive code here
}
string toastMessage = string.Format("Last Update: {0}", DateTime.Now);
// Launch a toast to show that the agent is running.
// The toast will not be shown if the foreground application is running.
ShellToast toast = new ShellToast();
toast.Title = toastTitle;
toast.Content = toastMessage;
toast.Show();
// If debugging is enabled, launch the agent again in one minute.
#if DEBUG_AGENT
ScheduledActionService.LaunchForTest(task.Name, System.TimeSpan.FromSeconds(60));
#endif
NotifyComplete();
}
}
}

Here we have the meat and potatoes of our scheduled task agent. We use the OnInvoke method call whenever we run the resource intensive task or the periodic task. To start we are going to create a String object toastTitle and make it blank. We then want to check and see what kind of task we are looking to run. We have one of two choices and those are a periodic or resource intensive. If it is a periodic task we then want to change our toastTitle to Update and create a ShellTile object called appTile which will reference our Live Tile.

We are then checking to see if the tile is empty or on our home screen and if it is not we are going to update the tile on the home screen. The only thing we are going to change on the live tile is the count. We want to show that our background agents are in fact working so we will change the live tile count from nothing to a one (1). We did this to show the capabilities of what is possible with background agents and live tiles but obviously more can be done than what we are doing here. After this is done we can update our live tile and exit our conditional statement. If it was a resource intensive task then we would run the code in the else statement.

Regardless of whether the task is periodic or resource intensive we want to also push a toast notification to the screen to let the user k now the last time the background agent ran. To do this we need to update the text that will appear in the toast notification. To do this we need to create a String object toastMessage to say something along the lines of string.Format(“Last Update: {0}”, DateTime.Now); but any old string you choose will do as well. After this we can create a new ShellToast and set the title to toastTitle and the content to toastMessage. After these have been completed we can show the toast notification, keeping in mind that the toast will not appear if we are still running the application.

You will notice next we have an if statement checking to see if we are debugging. If we are then we again want to speed up the wait time to somewhere around a minute to see that our application is functioning correctly. It was not mentioned above but code that we are using to check to see I we are debugging the application can be left in as it cannot be seen or used once we release our application into the marketplace. The last item in the OnInvoke method is the NotifyComplete method which is used to tell the OS that we have completed the intended operation for this run of the scheduled task.

// WP7DBStorage - WMAppManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.1">
<App xmlns="" ProductID="{b289718a-6391-4abf-a844-7e5c47f68008}" Title="Client Contact Information" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="WP7LDBStorage author" Description="Sample description" Publisher="WP7LDBStorage">
<IconPath IsRelative="true" IsResource="false">ApplicationIcon.png</IconPath>
<Capabilities>
<Capability Name="ID_CAP_GAMERSERVICES" />
<Capability Name="ID_CAP_IDENTITY_DEVICE" />
<Capability Name="ID_CAP_IDENTITY_USER" />
<Capability Name="ID_CAP_LOCATION" />
<Capability Name="ID_CAP_MEDIALIB" />
<Capability Name="ID_CAP_MICROPHONE" />
<Capability Name="ID_CAP_NETWORKING" />
<Capability Name="ID_CAP_PHONEDIALER" />
<Capability Name="ID_CAP_PUSH_NOTIFICATION" />
<Capability Name="ID_CAP_SENSORS" />
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
<Capability Name="ID_CAP_ISV_CAMERA" />
<Capability Name="ID_CAP_CONTACTS" />
<Capability Name="ID_CAP_APPOINTMENTS" />
</Capabilities>
<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
<ExtendedTask Name="BackgroundTask">
<BackgroundServiceAgent Specifier="ScheduledTaskAgent" Name="PeriodicTaskAgentBW" Source="PeriodicTaskAgentBW" Type="PeriodicTaskAgentBW.ScheduledAgent" />
</ExtendedTask>
</Tasks>
<Tokens>
<PrimaryToken TokenID="WP7LDBStorageToken" TaskName="_default">
<TemplateType5>
<BackgroundImageURI IsRelative="true" IsResource="false">Background.png</BackgroundImageURI>
<Count>0</Count>
<Title>Contact Info</Title>
</TemplateType5>
</PrimaryToken>
</Tokens>
</App>
</Deployment>

Above is the last piece of code we wanted to walk through, even though there is no coding involved. We are looking to thesection of the WMAppManifest.xml file. Here we can see that we have a BackgroundServiceAgent and it is referencing our Scheduled Task Agent and further down our .cs file in the Task project as well. This all happened when we added the project and this is how the project is able to find the Scheduled Task Agent.

This essentially concludes the background agent tutorial. We realize that not much was implemented in the tutorial but we have done this with a purpose. This being a 201 series and focused on the content contained within the Mango release, we want to see and hear about your implementations with background agents in your apps. Give us a comment below and let us know what applications you’re working on. If you also have any questions or comments they can be left below as well and we will get to the ASAP. Until the next Mango tutorial, Happy Hacking!

Share This