63

So, first I have read a ton of threads on this particular problem and I still do not understand how to fix it. Basically, I am trying to communicate with a websocket and store the message received in an observable collection that is bound to a listview. I know that I am getting a response back properly from the socket, but when it tries to add it to the observable collection it gives me the following error:

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

I've read some information on "dispatch" as well as some other things, but I am just massively confused! Here is my code:

public ObservableCollection<string> messageList  { get; set; }
private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
    {
        string read = "";
        try
        {
            using (DataReader reader = args.GetDataReader())
            {
                reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
                read = reader.ReadString(reader.UnconsumedBufferLength);
            }
        }
        catch (Exception ex) // For debugging
        {
            WebErrorStatus status = WebSocketError.GetStatus(ex.GetBaseException().HResult);
            // Add your specific error-handling code here.
        }


        if (read != "")
           messageList.Add(read); // this is where I get the error

    }

And this is the binding:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    //await Authenticate();
    Gameboard.DataContext = Game.GameDetails.Singleton;
    lstHighScores.ItemsSource = sendInfo.messageList;
}

How do I make the error go away while still binding to the observable collection for my listview?

Yecats
  • 1,715
  • 5
  • 26
  • 40

5 Answers5

131

This solved my issue:

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

Correct way to get the CoreDispatcher in a Windows Store app

various
  • 1,442
  • 1
  • 11
  • 5
  • 8
    fyi this is the equivalent to runOnUiThread for Android http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable) – kospol Jun 16 '15 at 23:57
  • is there any equivalent in python? – Nickpick Dec 16 '16 at 20:20
  • 3
    Can't believe, this was my EXACT problem and it was number one in the Google search results after searching for the entire Exception message. Good one! – Emanuel Vintilă Jan 15 '17 at 16:38
  • 1
    @nickpick - Not really... This solves a very specific problem for Windows applications, in C#. – ndbroadbent May 16 '17 at 16:56
  • 3
    I'm very new to C# and Windows development, but this is a very long chain of method calls, and kind of feels like a hack. Is there a better way to do this? – ndbroadbent May 16 '17 at 17:10
  • 1
    This is all well and good but this doesn't help with a Task based async scenario, and if you need to wait for a value to be returned from within the part that says "Your UI update code goes here!", you can't. I have added another answer which does allow for this. – Christian Findlay Mar 19 '18 at 08:51
9

Try replacing

messageList.Add(read); 

with

Dispatcher.Invoke((Action)(() => messageList.Add(read)));

If you're calling from outside your Window class, try:

Application.Current.Dispatcher.Invoke((Action)(() => messageList.Add(read)));
Baldrick
  • 11,712
  • 2
  • 31
  • 35
  • 1
    What namespace does Dispatcher live in? It reolves to "System.ServiceModel.Dispatcher" but Invoke is not a method of it. – Yecats Oct 13 '13 at 16:49
  • 2
    When used on the Page (or in any DependencyObject), call Dispatcher.RunAsync(() => messageList.Add(read)); Elsewhere call Window.Current.Dispatcher.RunAsync(() => messageList.Add(read)); – HDW Production Oct 14 '13 at 09:17
7

Slight modification for task based async methods but the code in here will not be awaited.

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

This code WILL await, and will allow you to return a value:

    private async static Task<string> GetPin()
    {
        var taskCompletionSource = new TaskCompletionSource<string>();

        CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
        async () =>
        {
            var pin = await UI.GetPin();
            taskCompletionSource.SetResult(pin);
        }
        );

        return await taskCompletionSource.Task;
    }

And on Android:

    private async Task<string> GetPin()
    {
        var taskCompletionSource = new TaskCompletionSource<string>();

        RunOnUiThread(async () =>
        {
            var pin = await UI.GetPin();
            taskCompletionSource.SetResult(pin);
        });

        return await taskCompletionSource.Task;
    }
Christian Findlay
  • 6,770
  • 5
  • 51
  • 103
2

In Xamarin, I got around this by using:

Device.BeginInvokeOnMainThread(() => {
   // code goes here
});
DevDave
  • 6,700
  • 12
  • 65
  • 99
0

Maby this is not a "good" practice, but it works.. I leave a message from webSocket, to mainBody instance, where I have a timered reader...

public class C_AUTHORIZATION
{
    public Observer3.A_MainPage_cl parentPageInstance; //еще одни экземпляр родителя
    public WebSocket x_Websocket; 
    private string payload = "";
    private DateTime nowMoment = DateTime.Now;
    public void GET_AUTHORIZED()
    {
       bitfinex_Websocket= new WebSocket("wss://*****.com/ws/2");

        var apiKey = "";
        var apiSecret = "";
        DateTime nowMoment = DateTime.Now;            

        payload = "{}";            

        x_Websocket.Opened += new EventHandler(websocket_Opened);                                                         
       x_Websocket.Closed += new EventHandler(websocket_Closed);  
    }
    
    void websocket_Opened(object sender, EventArgs e)
    {
        x_Websocket.Send(payload);
        parentPageInstance.F_messager(payload);
    }
    
    void websocket_Closed(object sender, EventArgs e)
    {
        parentPageInstance.F_messager("L106 websocket_Closed!");
        GET_AUTHORIZED();  
    }
   

}

public sealed partial class A_MainPage_cl : Page
{      
    DispatcherTimer ChartsRedrawerTimer;
    public bool HeartBeat = true;
   
    private string Message;        
    public A_MainPage_cl()
    {
       this.InitializeComponent();
      
        ChartsRedrawerTimer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 100) }; 
        ChartsRedrawerTimer.Tick += Messager_Timer;
        ChartsRedrawerTimer.Start();

        
    }        
         
    private void Messager_Timer(object sender, object e)
    {            
        if(Message !=null) // 
        {
            F_WriteLine(Message);
            Message = null; //  

        } 
    }
    public void F_messager(string message) // 
    { 
        Message = message; 
    }