UPDATE May 2, 2014: I mentioned using the emulator’s functionality to test geofencing in the initial blog post with placing multiple pins. It seems (after asking around internally at Microsoft) the Route option is the more reliable option to use in this case, as it simulates real behavior more accurately. Placing pins and jumping around on the map may conflict with the location optimization, resulting in unreliable trigger behavior.

Setting up our solution

With Windows Phone 8.1, we now have a range of ways to trigger a background task of our app. Aside from the previously available periodic timer that could trigger a background task, we can now also use raw push notifications, system events, device use triggers and location. The location method is used to trigger a background task using geofence, which we’ll be walking through in this post.

Naturally we’ll start out with a new Universal App to create a Windows and Windows Phone 8.1 app that share the same code base. For the purposes of this post, we’ll focus on Windows Phone 8.1, but the same principles can be applied to the Windows app. After creating our solution, we need to add an additional project that holds our background task. This is where the Windows Runtime Components come in, as the common runtime between Windows and Windows Phone 8.1 is now WinRT. Once created, add a reference to the new Windows Runtime Component from our WindowsPhone project.

Implement the background task

Delete the annoying Class1.cs file and create a new class. This class will inherit from IBackgroundTask to indicate it’s going to be a background task. This also requires us to implement the Run method, which will be called when our background task runs.

public void Run(IBackgroundTaskInstance taskInstance) 1 public void Run ( IBackgroundTaskInstance taskInstance )

The following is sort of up to you, but I like to have three static methods on my background task class to register, unregister and check if it’s registered. Your implementation might vary (especially considering the SystemCondition you can apply to the task) The code I’m using is as follows:

static string TaskName = "MyLocationTask"; public async static void Register() { if (!IsTaskRegistered()) { var result = await BackgroundExecutionManager.RequestAccessAsync(); var builder = new BackgroundTaskBuilder(); builder.Name = TaskName; builder.TaskEntryPoint = typeof(LocationTask).FullName; builder.SetTrigger(new LocationTrigger(LocationTriggerType.Geofence)); builder.Register(); } } public static void Unregister() { var entry = BackgroundTaskRegistration.AllTasks.FirstOrDefault(kvp => kvp.Value.Name == TaskName); if (entry.Value != null) entry.Value.Unregister(true); } public static bool IsTaskRegistered() { var taskRegistered = false; var entry = BackgroundTaskRegistration.AllTasks.FirstOrDefault(kvp => kvp.Value.Name == TaskName); if (entry.Value != null) taskRegistered = true; return taskRegistered; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 static string TaskName = "MyLocationTask" ; public async static void Register ( ) { if ( ! IsTaskRegistered ( ) ) { var result = await BackgroundExecutionManager . RequestAccessAsync ( ) ; var builder = new BackgroundTaskBuilder ( ) ; builder . Name = TaskName ; builder . TaskEntryPoint = typeof ( LocationTask ) . FullName ; builder . SetTrigger ( new LocationTrigger ( LocationTriggerType . Geofence ) ) ; builder . Register ( ) ; } } public static void Unregister ( ) { var entry = BackgroundTaskRegistration . AllTasks . FirstOrDefault ( kvp = > kvp . Value . Name == TaskName ) ; if ( entry . Value != null ) entry . Value . Unregister ( true ) ; } public static bool IsTaskRegistered ( ) { var taskRegistered = false ; var entry = BackgroundTaskRegistration . AllTasks . FirstOrDefault ( kvp = > kvp . Value . Name == TaskName ) ; if ( entry . Value != null ) taskRegistered = true ; return taskRegistered ; }

Last, but not least, we need some logic for when our task will run. In this example, we’ll trigger a toast notification from the background task when it’s run (YES! that’s an option now on Windows Phone 8.1). Note that the logic that determines which report we filter from the GeofenceMonitor’s reports will vary for each implementation:

public void Run(IBackgroundTaskInstance taskInstance) { // Get the information of the geofence(s) that have been hit var reports = GeofenceMonitor.Current.ReadReports(); var report = reports.FirstOrDefault(r => (r.Geofence.Id == "MyGeofenceId") && (r.NewState == GeofenceState.Entered)); if (report == null) return; // Create a toast notification to show a geofence has been hit var toastXmlContent = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02); var txtNodes = toastXmlContent.GetElementsByTagName("text"); txtNodes[0].AppendChild(toastXmlContent.CreateTextNode("Geofence triggered toast!")); txtNodes[1].AppendChild(toastXmlContent.CreateTextNode(report.Geofence.Id)); var toast = new ToastNotification(toastXmlContent); var toastNotifier = ToastNotificationManager.CreateToastNotifier(); toastNotifier.Show(toast); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void Run ( IBackgroundTaskInstance taskInstance ) { // Get the information of the geofence(s) that have been hit var reports = GeofenceMonitor . Current . ReadReports ( ) ; var report = reports . FirstOrDefault ( r = > ( r . Geofence . Id == "MyGeofenceId" ) && ( r . NewState == GeofenceState . Entered ) ) ; if ( report == null ) return ; // Create a toast notification to show a geofence has been hit var toastXmlContent = ToastNotificationManager . GetTemplateContent ( ToastTemplateType . ToastText02 ) ; var txtNodes = toastXmlContent . GetElementsByTagName ( "text" ) ; txtNodes [ 0 ] . AppendChild ( toastXmlContent . CreateTextNode ( "Geofence triggered toast!" ) ) ; txtNodes [ 1 ] . AppendChild ( toastXmlContent . CreateTextNode ( report . Geofence . Id ) ) ; var toast = new ToastNotification ( toastXmlContent ) ; var toastNotifier = ToastNotificationManager . CreateToastNotifier ( ) ; toastNotifier . Show ( toast ) ; }

NOTE: If you plan on executing asynchronous code in your background task with async/await, make sure to use the IBackgroundTaskInstance.GetDeferral method to inform the system your task might continue to run after the Run method returns.

Implement the app logic

There’s a few changes we need to make to the Package.appxmanifest to set up our app for the background task using geofence:

As we’re creating a toast notification in the task, change Application > Notifications > Toast capable to Yes

to Yes To be able to use geofencing, we need to enable the Location capability under Capabilities > Capabilities

capability under Lastly, we need to add a Background Tasks Declaration under Declarations > Available Declarations, ticking the Location Property and setting our Entry point to the full namespace of our task (e.g. GeofenceUniversalApp.Tasks.LocationTask)

Now that we’ve set up our manifest, we can start adding the logic to create and remove Geofences. Note I’ve also included a method to retrieve all current created geofences, so I can show them in my app. The code I’m using (in a static class) in this example is:

public static IList<Geofence> GetGeofences() { return GeofenceMonitor.Current.Geofences; } public static void CreateGeofence(string id, double lat, double lon, double radius) { if (GeofenceMonitor.Current.Geofences.SingleOrDefault(g => g.Id == id) != null) return; var position = new BasicGeoposition(); position.Latitude = lat; position.Longitude = lon; var geocircle = new Geocircle(position, radius); MonitoredGeofenceStates mask = 0; mask |= MonitoredGeofenceStates.Entered; // Create Geofence with the supplied id, geocircle and mask, not for single use // and with a dwell time of 5 seconds var geofence = new Geofence(id, geocircle, mask, false, new TimeSpan(0, 0, 5)); GeofenceMonitor.Current.Geofences.Add(geofence); } public static void RemoveGeofence(string id) { var geofence = GeofenceMonitor.Current.Geofences.SingleOrDefault(g => g.Id == id); if (geofence != null) GeofenceMonitor.Current.Geofences.Remove(geofence); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static IList < Geofence > GetGeofences ( ) { return GeofenceMonitor . Current . Geofences ; } public static void CreateGeofence ( string id , double lat , double lon , double radius ) { if ( GeofenceMonitor . Current . Geofences . SingleOrDefault ( g = > g . Id == id ) != null ) return ; var position = new BasicGeoposition ( ) ; position . Latitude = lat ; position . Longitude = lon ; var geocircle = new Geocircle ( position , radius ) ; MonitoredGeofenceStates mask = 0 ; mask |= MonitoredGeofenceStates . Entered ; // Create Geofence with the supplied id, geocircle and mask, not for single use // and with a dwell time of 5 seconds var geofence = new Geofence ( id , geocircle , mask , false , new TimeSpan ( 0 , 0 , 5 ) ) ; GeofenceMonitor . Current . Geofences . Add ( geofence ) ; } public static void RemoveGeofence ( string id ) { var geofence = GeofenceMonitor . Current . Geofences . SingleOrDefault ( g = > g . Id == id ) ; if ( geofence != null ) GeofenceMonitor . Current . Geofences . Remove ( geofence ) ; }

And that’s it! Now all we need to do is create a geofence in our app and register our background task. You can use the Windows Phone emulator’s tools to simulate the location of the device. If you trigger a pin outside the geofence area and then inside, the background task should trigger the toast notification. One side-note I do want to make is that it sometimes takes more than the 5 seconds of dwell time I’ve specified in the app logic for the task to trigger and that trying to trigger it multiple times in quick succession does not always work.

As always, the full solution can be downloaded below and reach me through the comments or on Twitter for questions, suggestions and/or feedback!

Downloads