67

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

The Invoke method does not exist.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ofer
  • 721
  • 1
  • 8
  • 7
  • 8
    Note to future readers: Remember, that if your app has multiple windows - there are multiple UI threads and dispatchers. – Filip Skakun Feb 17 '16 at 18:42

6 Answers6

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;
Community
  • 1
  • 1
Cœur
  • 37,241
  • 25
  • 195
  • 267
  • I get System.NotImplementedException when using this.I access it from UI thread. – Naren Oct 09 '13 at 09:29
  • Never had this Exception here. It could be from somewhere else, like inside your block instructions. – Cœur Oct 09 '13 at 12:15
  • I get this exception when I tried to access the Dispatcher .I haven't executed any code using the dispatcher. – Naren Oct 09 '13 at 12:30
  • 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`? – Cœur Oct 09 '13 at 15:48
  • 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](http://stackoverflow.com/q/19264676/2256349). – Naren Oct 09 '13 at 16:21
  • According to the other thread, your solution was using `Deployment.Current.Dispatcher`. – Cœur Oct 10 '13 at 12:06
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.

Magnus Johansson
  • 28,010
  • 19
  • 106
  • 164
Larry Osterman
  • 16,086
  • 32
  • 60
  • 5
    Is there a way to get dispatcher on non-UI thread ? Currently i get null from CoreWindow.GetForCurrentThread() – Grigory Jun 12 '12 at 14:27
  • 3
    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. – Larry Osterman Jun 14 '12 at 13:06
  • 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. – Habba Jul 02 '12 at 11:55
  • 1
    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. – Larry Osterman Jul 02 '12 at 14:02
  • [an example for this approach](http://blogs.msdn.com/b/dave_crooks_dev_blog/archive/2012/07/19/multi-threaded-dynamic-data-and-metro-part-1-introducing-async-await-and-the-folder-picker.aspx) – Korki Korkig Aug 01 '13 at 08:08
  • 4
    @Larry: You can get the Dispatcher from the background thread, see here: http://stackoverflow.com/a/25760799/543303 `CoreApplication.MainView.CoreWindow.Dispatcher` – eFloh Sep 10 '14 at 08:36
  • @Grigory look at this answer: http://stackoverflow.com/questions/16477190/correct-way-to-get-the-coredispatcher-in-a-windows-store-app/18485317#18485317 – MAXE Apr 01 '15 at 13:15
8

Use:

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

It works for me.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 5
    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. – Luke Kim Dec 15 '12 at 00:56
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);
Deeb
  • 307
  • 5
  • 6
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.

David Rettenbacher
  • 5,088
  • 2
  • 36
  • 45
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();
George Birbilis
  • 2,782
  • 2
  • 33
  • 35