Run code on UI thread in WinRT
Asked Answered
H

6

67

How can I run code on the UI thread in WinRT (Windows 8 Metro)?

The Invoke method does not exist.

Habitation answered 14/5, 2012 at 7:21 Comment(1)
Note to future readers: Remember, that if your app has multiple windows - there are multiple UI threads and dispatchers.Sanderson
W
81

It's easier to directly get the CoreWindow from the non-UI thread. The following code will work everywhere, even when GetForCurrentThread() or Window.Current returns null.

CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
    <lambda for your code which should run on the UI thread>);

for example:

CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
    () =>
    {
        // Your UI update code goes here!
    });

You'll need to reference Windows.ApplicationModel.Core namespace:

using Windows.ApplicationModel.Core;
Willow answered 26/6, 2013 at 8:32 Comment(6)
I get System.NotImplementedException when using this.I access it from UI thread.Specialize
Never had this Exception here. It could be from somewhere else, like inside your block instructions.Shaniceshanie
I get this exception when I tried to access the Dispatcher .I haven't executed any code using the dispatcher.Specialize
So CoreApplication.MainView.CoreWindow.Dispatcher doesn't work for you? Do you have an Exception when trying the other solutions around? Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher? this.Dispatcher?Shaniceshanie
I also tried Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher but that too threw exception.And I guess this.Dispatcher doesn't work with Windows.Runtime.I have posted it as a seperate question.Specialize
According to the other thread, your solution was using Deployment.Current.Dispatcher.Shaniceshanie
I
69

Use:

From your UI thread, execute:

var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;

From your background (non UI thread)

dispatcher.RunAsync(DispatcherPriority.Normal, 
    <lambda for your code which should run on the UI thread>);

That should work on both CP and later builds.

Intramuscular answered 15/5, 2012 at 5:2 Comment(7)
Is there a way to get dispatcher on non-UI thread ? Currently i get null from CoreWindow.GetForCurrentThread()Moncear
No. Dispatchers are tied to a UI thread, so you need to retrieve the dispatcher on the UI thread. Once the dispatcher has been retrieved, you can remember it. If you're in a XAML application, then most UI objects have dispatcher member that you can use.Intramuscular
So which parts of my app are actually running in UI Thread? I'm using a FrameWorkView (Windows::ApplicationModel::Core::IFrameworkView), and not able to use the dispatcher I acquired from Run()-method. I'm getting WrongThreadException when I'm trying to create MediaElement through RunAsync.Magdeburg
Your original code runs on the UI thread, as do any of the XAML based event callbacks). If you use the "await" keyword from a UI thread, all the code after the "await" keyword will run on the UI thread. However event callbacks from other WinRT APIs might not be on the UI thread, if they're not, you use the dispatcher to get back to the UI thread. If you get the WrongThreadException, it probably means that the WinRT API came in on a non UI thread.Intramuscular
an example for this approachMethacrylate
@Larry: You can get the Dispatcher from the background thread, see here: https://mcmap.net/q/296921/-global-dispatcher-in-winrt CoreApplication.MainView.CoreWindow.DispatcherDachshund
@Moncear look at this answer: #16477690Avoid
U
8

Use:

this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Frame.Navigate(typeof(Welcome), this));

It works for me.

Unapproachable answered 14/9, 2012 at 7:32 Comment(1)
This does not actually guarantee to run it on the UI thread. It only will if "this" is a an object in the UI context.Fourteenth
G
5

This is a much easier way in my opinion.

Get the TaskScheduler associated with the UI.

    var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();

Then start a new Task and on the above UISyncContext.

    Task.Factory.StartNew(() => { /* Do your UI stuff here; */}, new System.Threading.CancellationToken(), TaskCreationOptions.PreferFairness, UISyncContext);
Graner answered 12/10, 2012 at 4:18 Comment(3)
FYI: I get an InvalidOperationException "The current SynchronizationContext may not be used as a TaskScheduler".Hydrophane
I also get an InvalidOperationFestus
Your SynchronizationContext is null. That's why you are getting that exception. Nothing wrong with my code.Graner
F
0

DispatcherTimer is also an option.

I used it for code that must be run in Xaml-designer (CoreWindow.Dispatcher,... are not available in UWP-designer)

var localTimer = new DispatcherTimer
{
    Interval = TimeSpan.FromMilliseconds(0)
};
localTimer.Tick += (timer, e) =>
{
    (timer as DispatcherTimer).Stop();
    action();
};
localTimer.Start();

Disclaimer:
I should note that this should be a last-resort option if every other fails.

Fender answered 28/11, 2016 at 17:39 Comment(0)
U
0

On UWP, I was having problem trying to set the Source property of CaptureElement control (that is defined in XAML), it was complaining about being prepared at different thread, even though I was trying to set it from code that was invoked via a Page_Loaded event handler. I ended up using this to work around it:

previewControl.Dispatcher.TryRunAsync(CoreDispatcherPriority.Normal, () => {
   previewControl.Source = _mediaCapture;
}).GetAwaiter().GetResult();
Unknow answered 24/8, 2017 at 13:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.