Hands-OnLab

AddingMultitaskingtoYourApplication

Labversion:1.0.0

Lastupdated:11/7/2018

Contents

Overview

Exercise

Task 1 – Pinning Project Tiles to the Start Area

Task 2 – Implementing a Background Agent

Summary

Overview

TheoriginalWindows®Phonedevelopertoolsdidnotallowyourapplicationstoperformoperationswhileinactive.Thislimitedtherangeofexperiencesyoucoulddeliverinyourapplication.WindowsPhoneCodenamed Mangoallowsyourapplicationtoperformoperationsevenwhileinactivebyusingbackgroundagents.Backgroundagentsarecreatedbyaddinganewtypeofprojecttoyourapplication’ssolutioninordertocontaintheagent’slogic.Yourapplicationcanthenregisterthebackgroundagentwiththeoperatingsystemandhaveitscheduletheagenttorunwhileyourapplicationisdormant.ThiseffectivelyallowsformultitaskinginapplicationsyoudevelopusingWindowsPhoneCodenamed Mango.Inaddition,WindowsPhoneCodenamed Mangoallowsanapplicationtopinmultipletilesassociatedwithit,allofwhichleadtodifferentlocationsinsidetheapplicationwhentapped.

Thislabusesthe“Tidy”applicationtodemonstratethesenewfeaturesbygoingthroughthenecessarystepsforimplementingandregisteringabackgroundagentonbehalfofyourapplication.Wewilluseabackgroundagentinordertoupdatetheapplication’stileswhileitisnotactive.

Objectives

Thislabprovidesinstructionstohelpyouachievethefollowing:

  • Understandhowtopinmultipleapplicationtilestothestartarea
  • Understandhowtomanageanapplication’spinnedtiles
  • Implementabackgroundagentinyourapplication

Prerequisites

Thefollowingprerequisiteswillensurethatyougainmostyoucanfromthishands-onlab:

  • MicrosoftVisualStudio2010orMicrosoftVisualC#Express2010,andtheWindows®PhoneDeveloperToolsavailableat
  • KnowledgeonhowtocreateapplicationsforWindowsPhone7

LabStructure

Thislabincludescontainsasingleexercisewiththefollowingtasks:

  1. Pinningprojecttilestothestartareaandmanagingaproject’spinnedtiles
  2. Creatinganewbackgroundagentproject,addingtheagent’slogicandexposingthebackgroundagentthroughyourapplication

Estimatedcompletiontime

Completingthislabshouldtakebetween30and50minutes.

Exercise

Inthisexercise,weshowhowtoaddsecondarytilesrepresentingspecificprojectstothemainscreen.Wethencreateabackgroundagenttoupdatethepinnedprojecttiles.

Task1–PinningProjectTilestotheStartArea

  1. OpenthestartersolutionfoundlocatedinthelabinstallationfolderunderSource\Begin.
  2. LocatetheTodo.BusinessprojectandcreateanewclassnamedShellTileHelpersCoreundertheShellprojectfolder.Maketheclassstatic:

C#

publicstaticclassShellTileHelpersCore

{

//…

}

  1. Addthefollowingusingstatementstothenewlycreatedclassfile:

C#

usingMicrosoft.Phone.Shell;

usingSystem.Linq;

  1. Thisclasshelpsustoencapsulatetilepinningandunpinningfunctionality. CreateaPinmethodthatwillallowustopintilestothedevice’sstartarea:

C#

publicstaticvoidPin(Uriuri,ShellTileDatainitialData)

{

//Createthetileandpintostart.Thiswillcausetheapptobe

//deactivated

ShellTile.Create(uri,initialData);

}

ShellTileisaclassfromtheMicrosoft.Phone.Shellnamespace,whichisresponsibleformanagingprimaryandsecondarytilesfortheapplication.Theclassprovidesanumberofstaticmethods,whichhelpstocreate/removetilesandaccesstoacollectioncontainingalloftheapplication’stiles,whichcanbeusedtolookforspecifictile.EachapplicationtilehastospecifythenavigationURItofollowwhentheusertapsthetileonthemainscreenaswellasadditionaldataintheformofaShellTileDatainstance,whichdefinesthetile’slook.

Note:WeexaminetheShellTileDataclassanditspropertieslaterinthistask.

  1. AddtwooverloadsforanUnPinmethodusingthefollowingcodesnippet:

C#

publicstaticvoidUnPin(stringid)

{

varitem=ShellTile.ActiveTiles.FirstOrDefault

(x=>x.NavigationUri.ToString().Contains(id));

if(item!=null)

item.Delete();

}

publicstaticvoidUnPin(Uriuri )

{

varitem=ShellTile.ActiveTiles.FirstOrDefault

(x=>x.NavigationUri==uri);

if(item!=null)

item.Delete();

}

Toremoveapinnedtileweneedtolocateitfirst,andthenexecuteDeletefunctionoffoundtile.BothmethodsusetheShellTile.ActiveTilespropertytotrytolocatethespecifiedtile.Ifthetileislocated,wedeleteit.

  1. Addtwoadditionalmethodstotheclass.ThesewillbesimilarinnaturetotheUnPinmethodbutwillbeusedtocheckwhetheracertainprojecthasatileinplaceornot:

C#

publicstaticboolIsPinned(Uriuri)

{

varitem=ShellTile.ActiveTiles.FirstOrDefault

(x=>x.NavigationUri==uri);

returnitem!=null;

}

publicstaticboolIsPinned(stringuniqueId)

{

varitem=ShellTile.ActiveTiles.FirstOrDefault

(x=>x.NavigationUri.ToString().Contains(uniqueId));

returnitem!=null;

}

  1. SavethefileandnavigatetotheTodoproject.LocatetheprojectfoldernamedPushandaddanewclassnamedShellTileHelpersUIunderit.MaketheclassstaticandmakesureitisapartoftheTodonamespace(bydefaultitiscreatedundertheTodo.Pushnamespace):

C#

namespaceTodo

{

publicstaticclassShellTileHelpersUI

{

//…

}

}

ThisclasswillusefunctionalityweintroducedinShellTileHelpersCoretohelpmanagePin/UnpinfunctionalitythroughtheUI.

  1. Ouraimistocreatetilesthatrepresentasingleprojecteachandprovideabitofinformationabouttheprojectataglance.Asmentionedbefore,eachtileshouldhaveaURIleadingtoapagedisplayingitsassociatedproject.AddthefollowingextensionmethodtoeasilycreateaURIfromagivenproject:

C#

publicstaticUriMakePinnedProjectUri(thisProjectp)

{

returnUIConstants.MakePinnedProjectUri(p);

}

Note:ToseetheimplementationforMakePinnedProjectUri,navigatetotheUIConstants.csfileundertheMiscfolder.

  1. AddanadditionalmethodtocreateaURIleadingtoatilebackgroundimagethatmatchesaspecifiedproject’scolor.

C#

publicstaticUriGetDefaultTileUri(thisProjectproject)

{

stringcolor=ApplicationStrings.ColorBlue;//defaulttoblue

ColorEntryListlist=App.Current.Resources[UIConstants.ColorEntries]as

ColorEntryList;

if(list!=null)

{

ColorEntryprojectEntry=list.FirstOrDefault(x=>x.Color==

project.Color);

if(projectEntry!=null)

color=projectEntry.Name;

}

returnUIConstants.MakeDefaultTileUri(color);

}

  1. Addamethodtopinaprojecttothestartarea.Forthis,werequireaShellTileDatavalueandastheclassisabstract,wewillusetheStandardTileDataclassthatderivesfromit.CreatethePinProjectmethodaccordingtothefollowingcode:

C#

publicstaticvoidPinProject(Projectp)

{

//Createtheobjecttoholdthepropertiesforthetile

StandardTileDatainitialData=newStandardTileData

{

//Definethetile’stitleandbackgroundimage

BackgroundImage=p.GetDefaultTileUri(),

Title=p.Name

};

Uriuri=p.MakePinnedProjectUri();

ShellTileHelpersCore.Pin(uri,initialData);

}

  1. TheUnPinProjectmethodwillsimplypassaproject’sIDtotheUnPinmethodwevreatedpreviously:

C#

publicstaticvoidUnPinProject(Projectp)

{

ShellTileHelpersCore.UnPin(p.Id.ToString());

}

  1. Similarly,IsPinnedwillrelyonpreviousShellTileHelpersCoreimplementations:

C#

publicstaticboolIsPinned(thisPhoneApplicationPagepage)

{

returnShellTileHelpersCore.IsPinned(

page.NavigationService.CurrentSource);

}

publicstaticboolIsPinned(thisProjectproject)

{

Uriuri=project.MakePinnedProjectUri();

returnShellTileHelpersCore.IsPinned(project.Id.ToString());

}

  1. SavetheclassandnavigatetotheProjectDetailsView.xaml.csfileundertheViews\Projectsubfolder.ThisclassalreadyhasamethodnamedappBar_OnPinProject.Thismethodisaneventhandlermethod,executedwhentheusertapstheapplicationbarpin/unpinicon.Addthefollowingcodetothemethod’sbody:

C#

privatevoidappBar_OnPinProject(objectsender,EventArgse)

{

Projectproject=DataContextasProject;

if(project.IsPinned())

ShellTileHelpersUI.UnPinProject(project);

else

ShellTileHelpersUI.PinProject(project);

UpdateProjectPinIcons();

}

Thismethodeitherpinorunpinstheproject,dependingonitscurrentstate,andupdatestheapplicationbariconaccordingly.

  1. LocatethemethodnamedUpdateProjectPinIcons,whichisalreadypresentinthefile.Themethodiscurrentlyempty.Addthefollowingcodesnippettoinitializetheapplicationbariconandtextaccordingtotheproject’spinnedstatus:

C#

privatevoidUpdateProjectPinIcons()

{

if((DataContextasProject).IsPinned())

{

((ApplicationBarIconButton)ApplicationBar.Buttons[(int)Utils.ProjectDetailsViewAppBarButtons.PinProject]).Text=ApplicationStrings.appBar_UnPin;

((ApplicationBarIconButton)ApplicationBar.Buttons[(int)Utils.ProjectDetailsViewAppBarButtons.PinProject]).IconUri=newUri("/Images/appbar.unpin.png",UriKind.Relative);

}

else

{

((ApplicationBarIconButton)ApplicationBar.Buttons[(int)Utils.ProjectDetailsViewAppBarButtons.PinProject]).Text=ApplicationStrings.appBar_Pin;

((ApplicationBarIconButton)ApplicationBar.Buttons[(int)Utils.ProjectDetailsViewAppBarButtons.PinProject]).IconUri=newUri("/Images/appbar.pin.png",UriKind.Relative);

}

}

Thiscodeusestheapplication’sconstantstoretrievelocalizedtextaccordingtotheprojectpinstate.

  1. Locate the InitializePage method and add the highlighted code in the snippet below to the end of the method:

C#

private void InitializePage()

{

if (!pageInitialized)

{

Guid projectID =

NavigationContext.GetGuidParam(UIConstants.ProjectIdQueryParam);

DataContext = App.ProjectsViewModel.Items.FirstOrDefault(

p => p.Id == projectID);

if ((DataContext as Project).OverdueItemCount > 0)

{

textOverdueCount.Foreground = new SolidColorBrush(Colors.Red);

textOverdueDescription.Foreground = new

SolidColorBrush(Colors.Red);

}

// If we are looking at the default project, disable the deletion

// button

if (projectID == new Guid(Utils.ProjectIDDefault))

{

((ApplicationBarIconButton)ApplicationBar.Buttons[

(int)Utils.ProjectDetailsViewAppBarButtons.DeleteProject]).

IsEnabled = false;

}

UpdateProjectPinIcons();

ApplicationBar.IsVisible = true;

pageInitialized = true;

// Check if this was initialized via deep-link..

if (!NavigationService.CanGoBack)

{

ApplicationBarIconButton homeButton =

new ApplicationBarIconButton {

IconUri = new Uri ("/Images/appbar.home.png",

UriKind.Relative),

IsEnabled = true,

Text= ApplicationStrings.appBar_Home };

homeButton.Click += new EventHandler(homeButton_Click);

ApplicationBar.Buttons.Add ( homeButton ) ;

}

}

}

This new block of code handles a special case where the application is launched by pressing one of the pinned project tiles. When launching the application through one of the project tiles, the first page the user will see is the associated project’s details. Since the project details page is the first page seen, pressing the device’s back key will back out of the application instead of returning to the main menu. For this reason, when launched through a project pinned tile we will add a special button to the application bar that allows the user to return to the application’s main page.

  1. Savetheclass,compileandstarttheapplication.Navigatetotheprojectsmanagementscreen(bytappingthe“folder”icononthemainscreenapplicationbar)andcreateatleast2projectswithdifferentcolors.Createafewtasksandassignthemtothedifferentprojects.Yourprojectlistshouldnowlooklikethefollowingscreenshots:

Figure1

Projectsscreen

Taponprojecticontonavigateintotheprojectdetailsscreenandusethe“Pin”icontopintheproject:

Figure2

ProjectDetailsScreen

Figure3

Pinnedproject

  1. Pressthepinnedtiletoreachtheprojectpagedirectly:

Figure4

Projectdetailsscreen

  1. Nowthattheprojectispinned,seehowthe“Pin”iconchanged.Taptheunpiniconandnavigateawayfromtheapplication–youwillseethattheprojectwasunpinned:

Figure5

StartScreen

  1. Itispossibletopin/unpinmultipleprojectsusingtheprevioussteps:

Figure6

Multipleprojecttiles

  1. Thisconcludesthetask.Inthenexttask,wewilladdanewbackgroundagent,whichwillupdatethepinnedtiles.

Task 2 – Implementing a Background Agent

  1. Addanewprojecttothesolution.Usethe“WindowsPhoneTaskSchedulerAgent”templateandnameitTaskLocationAgent:

Figure7

Addingnewprojecttothesolution

  1. AddareferencetothisnewprojectfromtheTodoproject:

Figure8

AddingaReferencetotheBackgroundAgent

  1. OpentheWMAppManifest.xmlfilefromPropertiesfolderintheTodoprojectandseehowaddingareferencetoanagentprojectcausesthemanifesttoincludeanadditionalelementthatpointstothenewagentweadded:

XML

ExtendedTaskName="BackgroundTask"

BackgroundServiceAgentSpecifier="ScheduledTaskAgent"

Name="TaskLocationAgent"Source="TaskLocationAgent"

Type="TaskLocationAgent.TaskScheduler"/>

</ExtendedTask

  1. Beforeyouimplementtheagent,addsomecodetostartandstoptheagent.NavigatetotheSettingsViewModel.csfileundertheViewModelsfolder. CreatethefollowingclassmembersintheSettingsViewModelclass:

C#

PeriodicTaskperiodicTask=null;

conststringPeriodicTaskName="TidyPeriodic";

PeriodicTaskisaclassfromtheMicrosoft.Phone.Schedulernamespace,whichrepresentsascheduledtaskthatrunsregularlyforasmallamountoftime.Wewillusethesevariableslaterinthistask.

  1. LocatetheOnSavemethodandaddthefollowingcodebeforethelinethatcontains“SaveSettings();”:

C#

voidOnSave(objectparam)

{

if(UseBackgroundTaskUpdates||UseBackgroundLocation)

{

EnableTask(PeriodicTaskName,

ApplicationStrings.PeriodicTaskDescription);

}

else

DisableTask(PeriodicTaskName);

SaveSettings();

}

Thismethodwillusetwohelpermethods,whichwecreateinthenextsteps.

  1. AddtheEnableTaskmethod:

C#

voidEnableTask(stringtaskName,stringdescription)

{

PeriodicTaskt=this.periodicTask;

boolfound=(t!=null);

if(!found)

{

t=newPeriodicTask(taskName);

}

t.Description=description;

t.ExpirationTime=DateTime.Now.AddDays(10);

if (!found)

{

ScheduledActionService.Add(t);

}

else

{

ScheduledActionService.Remove(taskName);

ScheduledActionService.Add(t);

}

if (Debugger.IsAttached)

{

ScheduledActionService.LaunchForTest(t.Name, TimeSpan.FromSeconds(5));

}

}

Thismethodtriestolocateapreviouslycreatedperiodictaskandupdatesitsdescriptionandexpirationtime. Otherwise,abrandnewperiodictaskiscreated.Whether a new task is createdor not, a call is placed to launch the agent for debugging purposes if a debugger is attached. TheScheduledActionServiceclassenablesthemanagementofscheduledactions.

Note:Thetask“update”isactuallydonebedeletingtheoldtaskandaddinganewoneinitsstead.

  1. AddtheDisableTaskmethodtodisableapreviouslyenabledperiodictask:

C#

voidDisableTask(stringtaskName)

{

try

{

PeriodicTaskt;

if(periodicTask!=nullperiodicTask.Name!=taskName)

t=periodicTask;

else

t=periodicTask=ScheduledActionService.Find(taskName)

asPeriodicTask;

if(t!=null)

ScheduledActionService.Remove(t.Name);

}

finally{};

}

Likeinthepreviousstep,thiscodelooksforanexistingtaskandremovesitusingtheSchduledActionServiceclass.

  1. ModifytheSettingsViewModel’sconstructortopickuptheperiodictask’sstatusatinitialization–modifytheconstructoraccordingtothefollowingcodesnippet:

C#

publicSettingsViewModel()

{

syncProvider=newLocalhostSync();

syncProvider.DownloadFinished+=DownloadFinished;

syncProvider.UploadFinished+=UploadFinished;

syncProvider.DownloadUploadProgress+=OperationProgress;

periodicTask=ScheduledActionService.Find(PeriodicTaskName)

asPeriodicTask;

if(periodicTask!=null)

IsBackgroundProcessingAllowed=periodicTask.IsEnabled;

else

IsBackgroundProcessingAllowed=true;

LoadSettings();

}

  1. SavethisclassandreturntotheTaskLocationAgentproject.AddareferencetotheTodo.BusinessprojectintheTaskLocationAgentproject.
  2. NavigatetotheTaskScheduler.csfile.Letusreviewthisfile.Ithastwomethods: OnInvoke,calledwhenaperiodictaskisinvokedbythescheduledactionservice,andOnCancel,calledwhenanagentrequestiscanceled.WewillleavetheOnCanelimplementationasis.

Note:Thislabwillnotfocusonlocationupdates,whicharepartofthefullapplicationandareperformedusingthisbackgroundagent.Theendsolutionforthislabdoescontaintherelevantcode,butwewillnotcoveritaspartofthelab.

  1. Addthefollowingusingstatementtothefile:

C#

usingTodo.Misc;

  1. TheOnInvokemethodcheckswhichbackgroundupdateareenabledbyuserthroughtheapplication’ssettingsandrespondsaccordingly.AddthefollowingcodetotheOnInvokemethod:

C#

protectedoverridevoidOnInvoke(ScheduledTasktask)

{

SettingsWorkaroundpreferences=SettingsWorkaround.Load();

if(preferences==null)

{

NotifyComplete();

return;

}

if(preferences.UseTileUpdater)

DoTileUpdates(null);

this.NotifyComplete();

}

Thiscodeblockloadsthesettingsandiftilesupdatesenableditstartstileupdatenotificationprocedure.

  1. AddtheDoTileUpdatesmethodtotheclass:

C#

voidDoTileUpdates(objectununsed)

{

TaskProgressTileUpdaterupdater=newTaskProgressTileUpdater();

updater.Start();

}

ThismethodusesTaskProgressTileUpdaterclass,whichweaddlateron.

  1. AddTaskProgressTileUpdater.csandIBackgroundTaskHelper.cstotheTaskLocationAgentproject.BothfilescanbelocatedinthelabinstallationfolderundertheSources\Assetsfolder.
  2. TheIBackgroundTaskHelper.csfiledefinesaninterfacethatsupportscreatingmultiplebackgroundtaskhelperclassesandrunningthemfromabackgroundtaskagent.TaskProgressTileUpdaterimplementsthisinterface.ObserveitsimplementationoftheDoWorkmethod(partialcode):

C#

vartiles=ShellTile.ActiveTiles;

foreach(ShellTiletileintiles)

{

StandardTileDataupdatedData=newStandardTileData();

Projectproject=GetProject(tile);

if(project!=null)

{

intcount=GetOverdueCount(project);

stringcolor=GetColorName(project.Color);

if(count0)

{

updatedData.BackgroundImage=

newUri(string.Format("/Images/Tiles/{0}{1}.png",

color,count),UriKind.Relative);

}

else

{

updatedData.BackgroundImage=

newUri(string.Format("/Images/Tiles/{0}check.png",

color),UriKind.Relative);

}

updatedData.BackBackgroundImage=newUri(

string.Format("/Images/Tiles/{0}.png",

project.Color.Substring(1)),UriKind.Relative);

updatedData.BackContent=GetTasks(project);

tile.Update(updatedData);

}

}

Thiscodesnippetiteratesoverallpinnedapplicationtiles,calculatesthenumberofoverdueitemsinthetile’scorrespondingprojectandgetstheproject’scolor.Thesnippetthenupdatesthetilewithanimagegeneratedfromthegathereddata.Thetile’sbacksideisupdatedaswell.

Note:Atilecanusetwoimages,titlesandcontentsoneforeachofthetile’ssides.Ifbacksidepropertiesareset,thetilewillfliprandomlytopresentthedataonitsotherside.

Thehelpermethodsusedintheabovesnippet(GetProject,GetOverdureCount,GetColorName,etc.)generaterandomdata.Inrealworldapplicationthisdatacouldandshouldcomefromtheapplication’sSQLCEdatabase.

Note:IntheBetaversionofWindowsPhoneMangotools,theScheduledTaskAgentislimitedto5Mbofmemoryusageduetoaknownissue.Therefore,thislabcannotusetheapplication’sdatabasetogetactualprojectdataasthiswillcausetotheTaskAgenttousemorethan5Mbofmemory,terminatingtheagent.ThisissuewillberesolvedwiththenextversionofWindowsPhoneMangotools.

  1. Compileandruntheapplication.Navigateintosettingscreenandcheckthe“ShowOverdueTasks..”checkbox asshowninfigurebelow:

Figure9

Enablingthebackgroundagent

  1. Clickthesavebutton.Nowyoucannavigateawayfromtheapplication.Oncethebackgroundagentworksyourmainscreentileswillbeupdated:

Figure10

Updatedprojecttiles

  1. Youcancontrolthebackgroundservicesexecutedbyapplicationsviathephone’ssettings/applications/backgroundservicescreen:

Figure11

Phonebackgroundtaskssettingsscreen

ClickingonTidywillenableyoutoturnthebackgroundservicesforthisapplicationonoroff:

Figure12

Application’sbackgroundtaskssettings

  1. Thisconcludesthetaskandthelab.

Summary

Thislabhastakenyouthroughthenecessarystepsforcreatingabackgroundagenttoupdatetheapplication’stiles.Usingthisisanexample,youshouldnowhaveagreaterunderstandingofWindowsPhoneMango’snewmultitaskingcapabilitiesandshouldknowhowtoincorporatethesecapabilitiesintoyourfutureapplications.

Page | 1

©2010 Microsoft Corporation. All rights reserved.