question

HaythamKenway-0820 avatar image
0 Votes"
HaythamKenway-0820 asked NicoZhu-MSFT commented

How to use a CoreDispatcher with time-triggered BackgroundTask in uwp?

Hi there,

I am using C# and UWP to create a Windows store application.

In simple terms, the overall architecture is : View -> Controller -> (Database and BackgroundTask), every name represents a different module in the solution.

BackgroundTask is a WRC module and it registers a task that runs every 15 minutes using a time trigger and if there's a need, it'll update the database.

So if there are changes in the database and if the application is in foreground, i need to update the UI. After doing some research, i found that i need to use something called a Dispatcher as the backgroundTask and main UI Thread are using different memories and are not allowed to communicate with each other.

Now the problem is: Dispatcher is only available in View module and i need it in BackgroundTask module. Even if i pass in a reference by injecting an interface or something, when the time-trigger is gonna run the task, it'll start anew, meaning the starting point will be a class in the BackgroundTask (that implements IBackgroundTask interface) and hence, that instance of Task will not have the reference to the Dispatcher that i provided (as it'll be in the main UI thread).

Also, note that i cannot give direct reference of View to BackgroundTask as C# doesn't allow Cyclic Dependency.

I've been thinking about this for over a week now but couldn't find a solution.

Any help would be appreciated.

Thank you.

Edit:

this is how i'm registering the task:

 private readonly string EntryPoint = typeof(BackgroundTaskEntryPoint).FullName;
 internal async void Register()
         {
             if (!IsAlreadyRegistered(TaskName))
             {
                 BackgroundTaskBuilder TaskBuilder = new BackgroundTaskBuilder
                 {
                     Name = TaskName,
                     TaskEntryPoint = EntryPoint
                 };
                 TaskBuilder.SetTrigger(BackgroundTaskTrigger);
    
                 BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();
    
                 if (status == BackgroundAccessStatus.DeniedBySystemPolicy ||
                     status == BackgroundAccessStatus.DeniedByUser)
                 {
                     return;
                 }
    
                 var task = TaskBuilder.Register();
                 task.Completed += new BackgroundTaskCompletedEventHandler(Task_Completed);
             }
         }
    
         private void Task_Completed(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs args)
         {
             Debug.WriteLine("Task Completed event was raised");
         }

the trigger in this case is : [ToastNotificationHistoryChangedTrigger][1] and is being referred to as TOAST_STATUS_CHANGER in the following code snippet.

and this is the entry point:

 public sealed class BackgroundTaskEntryPoint : IBackgroundTask
     {
         private BackgroundTaskDeferral deferral;
         private readonly string QUARTER_HOUR_TOAST_SCHEDULER = 
             typeof(QuarterHourToastSchedulerBackgroundProcessRegistrar).Name;
         private readonly string USER_PRESENT_TOAST_SCHEDULER =
             typeof(UserPresentToastSchedulerBackgroundProcessRegistrar).Name;
         private readonly string TOAST_INTERACTION_HANDLER =
             typeof(ToastInteractionHandlerBackgroundProcessRegistrar).Name;
         private readonly string TOAST_STATUS_CHANGER =
             typeof(ToastNotificationDisplayedStatusChangerBackgroundProcessRegistrar).Name;
    
         public void Run(IBackgroundTaskInstance taskInstance)
         {
             deferral = taskInstance.GetDeferral();
             string taskName = taskInstance.Task.Name;
    
             if (taskName.Equals(QUARTER_HOUR_TOAST_SCHEDULER))
             {
                 new ReminderToastScheduler().Schedule();
             }
             else if (taskName.Equals(USER_PRESENT_TOAST_SCHEDULER))
             {
    
                 new MissedReminderToastScheduler().Schedule();
                 new ReminderToastScheduler().Schedule();
             }
             else if (taskName.Equals(TOAST_INTERACTION_HANDLER))
             {
                 new ToastInteractionHandler(
                     taskInstance.TriggerDetails as ToastNotificationActionTriggerDetail
                     )
                     .HandleInteraction();
             }
             else if (taskName.Equals(TOAST_STATUS_CHANGER))
             {
                 new ReminderStatusChanger().ChangeStatusToDisplayed();
             }
    
             deferral.Complete();
         }
    
     }

and this is the ReminderStatusChanger class:

 internal class ReminderStatusChanger
     {
         private SQLiteDatabase databaseInstance;
          
         internal ReminderStatusChanger()
         {
             databaseInstance = SQLiteDatabase.GetInstance();
         }
    
         internal void ChangeStatusToDisplayed()
         {
                 foreach (var toast in ToastNotificationManager.History.GetHistory())
                 {
                     try
                     {
                         databaseInstance.UpdateStatusToDisplayed(toast.Tag);
                     }
                     catch (Exception) { }
                 }
         }
   }


dotnet-csharpwindows-uwp
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thanks for the response.

I already tried achieving that using observer pattern but it didn't work.

In this scenario, when background task starts, it doesnt have anything in its memory. So it'll have to call the instance of the database class and make changes to it. And when this database class will try to send query messages, it won't be able to, as there's nothing observing it and it'll still need a Dispatcher to do the update.

I know this is a tricky situation so please let me know if you couldn't understand properly what I'm trying to explain.

0 Votes 0 ·

1 Answer

NicoZhu-MSFT avatar image
0 Votes"
NicoZhu-MSFT answered NicoZhu-MSFT commented

Hello, Welcome to Micorosoft Q&A,

So if there are changes in the database and if the application is in foreground, i need to update the UI.

Derive from your description, the key point is not related with CoreDispatcher. The key point is getting the signal when background triggered. BackgroundTask contains BackgroundTaskProgressEventHandler event, it will be invoked when the task instance progress value update in the backgroundtask. you could regard as a signal.

  task.Progress += new BackgroundTaskProgressEventHandler(OnProgress);

For more code please refer to BackgroundTask official code sample scenario5


If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


· 14
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you for this answer.

I read through the links that you provided and i'm still trying to figure out how this fits in my situation. In the code, their View and BackgroundTasks are in the same module and secondly, both the code and documentation are still using the Dispatcher to update UI.
Snippet from the code:

         /// <summary>
         /// Handle background task progress.
         /// </summary>
         /// <param name="task">The task that is reporting progress.</param>
         /// <param name="e">Arguments of the progress report.</param>
         private void OnProgress(IBackgroundTaskRegistration task, BackgroundTaskProgressEventArgs args)
         {
             var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
             {
                 var progress = "Progress: " + args.Progress + "%";
                 BackgroundTaskSample.ApplicationTriggerTaskProgress = progress;
                 UpdateUI();
             });
         }

And the documentation of BackgroundTaskCompletedEventArgs class is doing the same thing.

This means that even if I use BackgroundTaskProgress/CompletedEventHandler, I'll still have to get the reference to the View to update the UI.

Could you please explain a bit more considering my architecture?

Thank you.


0 Votes 0 ·

Yep, OnProgress event was invoked in the backgroundtask. To make sure the OnProgress event handler could access ui-thread, we need to call dispatcher to udpate UI interface. And
It is process communication when send back value from backgroundtask, if your backgroundtask is out process. So I said the key point is not dispatcher.


0 Votes 0 ·

Perfect. I completely understand this.

I have some other ways to do this as well without using the Progress/Completed Events. But this was never the problem.

this is the problem:

Now the problem is: Dispatcher is only available in View module and i need it in BackgroundTask module. Even if i pass in a reference by injecting an interface or something, when the time-trigger is gonna run the task, it'll start anew, meaning the starting point will be a class in the BackgroundTask (that implements IBackgroundTask interface) and hence, that instance of Task will not have the reference to the Dispatcher that i provided (as it'll be in the main UI thread).

0 Votes 0 ·
Show more comments