17

I'm working on a user control for UWP and it updates some of its visuals upon certain calls. However, since the core .NET library has been shifted around and the threading classes have been severely cut back, I don't know how to identify from the method in the user control if the calling thread is the UI thread or not so it knows whether or not to use the dispatcher to change a dependency property.

Anyone know how to do this?

EDIT: The dispatcher functionally can be "invoked" async fine on the UI thread - however, I really don't know if this is a good idea - to try to invoke on the main thread from the main thread via the dispatcher. If anyone has a reason this is fine or bad, that would also contribute to answering the question. If there's no reason to not use the dispatcher on the main thread, then I guess there's no problem.

ThisHandleNotInUse
  • 1,135
  • 1
  • 10
  • 23
  • Can't you just always call [RunAsync](https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.core.coredispatcher.runasync.aspx)? – Alexei Levenkov Aug 20 '15 at 02:18
  • 4
    @X-TECH unlikely correct duplicate - notice "uwp" tag which significantly changes available APIs. – Alexei Levenkov Aug 20 '15 at 02:20
  • @AlexeiLevenkov Yes, System.Thread is not available. – ThisHandleNotInUse Aug 20 '15 at 02:24
  • @x-Tech That answer utilizes System.Thread, an API not available in UWP – ThisHandleNotInUse Aug 20 '15 at 02:32
  • @AlexeiLevenkov Is this ok? I felt like it was a strange thing to try to be creating async tasks for the thread it's on... – ThisHandleNotInUse Aug 20 '15 at 02:33
  • I'd use it without thinking much, but Store apps is not area I have any reasonable experience - so can't say if there is better way. Generally so overhead for synchronous async tasks (when task is completed on the same thread synchronously) is relatively low. The only thing I'd do is bundle all updates into single call if possible. – Alexei Levenkov Aug 20 '15 at 02:47
  • IMHO always calling `RunAsync()` is fine. Frankly, IMHO it would be better to design the code so that property updates are done _only_ from the appropriate thread. It is generally easy to stay on the UI thread, except for background operations, pushing updates back to the UI thread. But if you find yourself in a situation where that can't work, just calling the `RunAsync()` or similar will work. See my related rant here: http://blogs.msmvps.com/duniho/2008/09/12/msdn-s-canonical-technique-for-using-control-invoke-is-lame/ – Peter Duniho Aug 20 '15 at 03:03
  • @PeterDuniho In this case I'm trying to rig up my singleton "console/logging" class which needs to be readily available from any thread and fires an event when a line is added, which, in turn, updates the viewmodel which must be done from the main thread so I can't be sure I'm always on the UI thread. I'm just working on setting up a testing environment for UWP for now and figured I'd make a control for my console singleton to display its feed so I don't have to rerig it if I use it in an application. – ThisHandleNotInUse Aug 20 '15 at 03:05

2 Answers2

22

I have found the solution...

CoreDispatcher.HasThreadAccess returns a bool indicating if you are on the UI thread or not.

ThisHandleNotInUse
  • 1,135
  • 1
  • 10
  • 23
  • 5
    It should be noted that in a UWP app, the UI thread is **not** Main thread. It's easy to check with debugger. For example, a breakpoint in the Loaded event handler of a page shows that Dispatcher.HasThreadAccess == true, and current thread is "Worker Thread" with "No name" and "Managed ID" == 3 – user4698855 Nov 28 '16 at 06:41
  • 6
    The `CoreDispatcher` can be accessed via `CoreWindow.GetForCurrentThread().Dispatcher`. Took me a little while to find :P – Felix Mar 01 '17 at 22:55
2

My son just encountered this as an Issue so I thought I would add an updated answer.

The Main UI thread can be accessed using the Core Dispatcher CoreWindow.GetForCurrentThread().Dispatcher or Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher

Below is a class that implements the Core Dispatcher and tests whether the calling thread is the Main UI Thread. If so it invokes the action, otherwise calls Dispatcher.RunAsync to execute it on the Main UI thread.

class Threading {

     private static CoreDispatcher Dispatcher => 
     Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;

     public static async void ThreadSafe(DispatchedHandler action)
        {
            // Calls Dispatcher.RunAsync to run a method on the Main UI Thread
            IAsyncAction UiThread(DispatchedHandler proc) => Dispatcher.RunAsync(CoreDispatcherPriority.Normal, proc);

            // Checks to see if this was called from the Main UI thread 
            // If we are in the Main UI thread then Invoke the action 
            // Otherwise: Send it to run in the Main Ui Thread.

            if (Dispatcher.HasThreadAccess) { action.Invoke(); } else { await UiThread(action); };
        }
    }

The above class could be used like so:

// Some event handler callback
private void SomeCallback(object sender)(){
    void line() => Frame.Navigate(typeof(PageName));
    Threading.ThreadSafe(line);
}

// Pass a handle to the control and a string to update it's text property
internal static void ChangeControlText(dynamic ctrl , string v)
    {
                void line() {ctrl.Text= v;}
                ThreadSafe(line);
    }