2
private void LogInButton_Click(object sender, RoutedEventArgs e)
{
    var api = new RestAPI("http://localhost:2624/", UsernameTextBox.Text, PasswordTextBox.Password);

    api.AutenticarUsuarioFinalizado += (o, args) =>
    {
        ProgressBar.IsIndeterminate = false;
        ProgressBar.Visibility = Visibility.Collapsed;
        LogInButton.IsEnabled = true;

        if (args.Error) return;

        if (args.Resultado.Autenticado)
        {
        }
    };

    api.AutenticarUsuario();
    ProgressBar.Visibility = Visibility.Visible;
    ProgressBar.IsIndeterminate = true;
    LogInButton.IsEnabled = false;
}

api.AutenticarUsuario(); calls a rest API asynchronously, when it's done it calls the event handler api.AutenticarUsuarioFinalizado and got this error in line ProgressBar.IsIndeterminate = false; because the call open a new thread, how can I fix it? the error is:

The application called an interface that was marshalled for a different thread.

svick
  • 236,525
  • 50
  • 385
  • 514
Geykel
  • 1,888
  • 1
  • 13
  • 15

2 Answers2

4

The problem is that your event handler doesn't execute on the UI thread. I think the best way to fix that is to convert your EAP (Event-based Asynchronous Pattern) method to TAP (Task-based Asynchronous Pattern) using TaskCompletionSource:

public static Task<Resultado> AutenticarUsuarioAsync(this RestAPI api)
{
    var tcs = new TaskCompletionSource<Resultado>();

    api.AutenticarUsuarioFinalizado += (sender, args) =>
    {
        if (args.Error)
            tcs.TrySetException(new SomeAppropriateException());
        else
            tcs.TrySetResult(args.Resultado);
    };

    api.AutenticarUsuario();

    return tcs.Task;
}

…

private async void LogInButton_Click(object sender, RoutedEventArgs e)
{
    var api = new RestAPI(
        "http://localhost:2624/", UsernameTextBox.Text,
        PasswordTextBox.Password);

    ProgressBar.Visibility = Visibility.Visible;
    ProgressBar.IsIndeterminate = true;
    LogInButton.IsEnabled = false;

    try
    {
        var resultado = await api.AutenticarUsuarioAsync();
        if (resultado.Autenticado)
        {
            // whatever
        }
    }
    catch (SomeAppropriateException ex)
    {
        // handle the exception here
    }
    finally
    {
        ProgressBar.IsIndeterminate = false;
        ProgressBar.Visibility = Visibility.Collapsed;
        LogInButton.IsEnabled = true;
    }
}

Because awaiting a Task will always resume on the original context, you're not going to get an exception this way. As an additional advantage, you don't have to write your code “inside-out”, like you do with EAP.

You should also consider using bindings, instead of setting the properties of your UI controls manually.

svick
  • 236,525
  • 50
  • 385
  • 514
3

Your AutenticarUsuarioFinalizado event handler is probably being executed in a thread different from the UI thread. You should only modify/create UI objects from the UI thread. Use a dispatcher in your event handler, check here: Run code on UI thread in WinRT

var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
api.AutenticarUsuarioFinalizado += (o, args) =>
{
   dispatcher.RunAsync(DispatcherPriority.Normal, () => 
       ProgressBar.IsIndeterminate = false;
       ProgressBar.Visibility = Visibility.Collapsed;
   [...]
Community
  • 1
  • 1
fcuesta
  • 4,429
  • 1
  • 18
  • 13