Update UI from thread in WinRT

17

Since the Windows 8 consumer preview was released a few days ago, I am working on the new WinRT (for Metro Applications) in C# and I had ported my self written IRC class to the new threading and networking.

The problem is: My class is running an thread for receiving messages from the server. If this happens, the thread is making some parsing and then firing an event to inform the application about this. The subscribed function then 'should' update the UI (an textblock).

This is the problem, the thread cannot update the UI and the invoker method that has worked with .NET 4.0 doesn't seem to be possible anymore. Is there an new workaround for this or even an better way to update the UI ? If I try to update the UI from the event subscriber i will get this Exception:

The application called an interface that was marshalled for a different thread (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

c#
windows-runtime
asked on Stack Overflow Mar 2, 2012 by Suchiman • edited Feb 6, 2013 by Anton Sizikov

3 Answers

28

The preferred way to deal with this in WinRT (and C# 5 in general) is to use async-await:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    string text = await Task.Run(() => Compute());
    this.TextBlock.Text = text;
}

Here, the Compute() method will run on a background thread and when it finishes, the rest of the method will execute on the UI thread. In the meantime, the UI thread is free to do whatever it needs (like processing other events).

But if you don't want to or can't use async, you can use Dispatcher, in a similar (although different) way as in WPF:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() => Compute());
}

private void Compute()
{
    // perform computation here

    Dispatcher.Invoke(CoreDispatcherPriority.Normal, ShowText, this, resultString);
}

private void ShowText(object sender, InvokedHandlerArgs e)
{
    this.TextBlock.Text = (string)e.Context;
}
answered on Stack Overflow Mar 2, 2012 by svick • edited Jun 12, 2012 by svick
8

Here is an easier way to do it I think!

First capture your UI SyncronizationContext with the following:

    var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();

Run your server call operation or any other background thread operation you need:

    Task serverTask= Task.Run(()=> { /* DoWorkHere(); */} );

Then do your UI operation on the UISyncContext you captured in first step:

    Task uiTask= serverTask.ContinueWith((t)=>{TextBlockName.Text="your value"; }, UISyncContext);
answered on Stack Overflow Oct 12, 2012 by Deeb
0

IMO I think "ThreadPool" is the recommended route.

https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465290.aspx

public static Task InvokeBackground(Func<Task> action)
    {
        var tcs = new TaskCompletionSource<bool>();

        var unused = ThreadPool.RunAsync(async (obj) =>
        {
            await action();
            tcs.TrySetResult(true);
        });

        return tcs.Task;
    }
answered on Stack Overflow Jul 15, 2015 by Quincy

User contributions licensed under CC BY-SA 3.0