Question

I have a method with some code that does an await operation:

public async Task DoSomething()
{
    var x = await ...;
}

I need that code to run on the Dispatcher thread. Now, Dispatcher.BeginInvoke() is awaitable, but I can't mark the lambda as async in order to run the await from inside it, like this:

public async Task DoSomething()
{
    App.Current.Dispatcher.BeginInvoke(async () =>
        {
            var x = await ...;
        }
    );
}

On the inner async, I get the error:

Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type.

How can I work with async from within Dispatcher.BeginInvoke()?

Was it helpful?

Solution

The other answer may have introduced an obscure bug. This code:

public async Task DoSomething()
{
    App.Current.Dispatcher.Invoke(async () =>
    {
        var x = await ...;
    });
}

uses the Dispatcher.Invoke(Action callback) override form of Dispatcher.Invoke, which accepts an async void lambda in this particular case. This may lead to quite unexpected behavior, as it usually happens with async void methods.

You are probably looking for something like this:

public async Task<int> DoSomethingWithUIAsync()
{
    await Task.Delay(100);
    this.Title = "Hello!";
    return 42;
}

public async Task DoSomething()
{
    var x = await Application.Current.Dispatcher.Invoke<Task<int>>(
        DoSomethingWithUIAsync);
    Debug.Print(x.ToString()); // prints 42
}

In this case, Dispatch.Invoke<Task<int>> accepts a Func<Task<int>> argument and returns the corresponding Task<int> which is awaitable. If you don't need to return anything from DoSomethingWithUIAsync, simply use Task instead of Task<int>.

Alternatively, use one of Dispatcher.InvokeAsync methods.

OTHER TIPS

I think you can use below code and then depends of place use it with async and await or without to fire and forget:

public static Task FromUiThreadAsync(Action action)
{
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    Dispatcher disp = GetUiDispatcher();
    
    disp.Invoke(DispatcherPriority.Background, new Action(() =>
    {
        try
        {
            action();
            tcs.SetResult(true);
        }
        catch (Exception ex)
        {
            tcs.SetException(ex);
        }
    }));

    return tcs.Task;
}

Use Dispatcher.Invoke()

public async Task DoSomething()
{
    App.Current.Dispatcher.Invoke(async () =>
    {
        var x = await ...;
    });
}

(Edit: This answer is wrong, but I'll fix it soon)

Declare this

public async Task DoSomethingInUIThreadAsync(Func<Task> p)
{
  await Application.Current.Dispatcher.Invoke(p);
}  

Use like this


string someVar = "XXX";

DoSomethingInUIThreadAsync(()=>{

 await Task.Run(()=> {
     Thread.Sleep(10000);
     Button1.Text = someVar;
  });

});

DoSomethingInUIThreadAsync receives a delegate that returns a Task, Application.Current.Dispatcher.Invoke accepts a Func callback that can be awaited.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top