3

I try to update ObservableCollection asynchronously using the following code in my WPF project:

if (Dispatcher.Thread != Thread.CurrentThread)
{
    if (Dispatcher.Thread.ThreadState != ThreadState.Stopped && !Dispatcher.Thread.IsBackground)
    {
        Dispatcher.Invoke(new Action(() => { ChangeCollectionByAction(action); }), null);
    }
    else
    {
        var op = Dispatcher.BeginInvoke(new Action(() => { ChangeCollectionByAction(action); }), null);
        var status = op.Status;
        while (status != DispatcherOperationStatus.Completed)
        {
            status = op.Wait(TimeSpan.FromSeconds(1));
        }
    }
}

But unfortunetly status alway equals DispatcherOperationStatus.Pending.

p.s.: may be my problem that i use ElementHost on WinForms project?

IAbstract
  • 19,551
  • 15
  • 98
  • 146
Sergey Shulik
  • 950
  • 1
  • 9
  • 24
  • 4
    Why aren't you just using `Dispatcher.Invoke`? There's no reason to use BeginInvoke and then immediately wait on the results... – Reed Copsey Mar 26 '12 at 18:07
  • @Reed Copsey, loop with getting status of DispatecherOperation only for demonstration, that status is always Pedding. – Sergey Shulik Mar 27 '12 at 06:44
  • Is your dispatcher thread a background thread? – Tudor Mar 29 '12 at 16:24
  • Yes, using ElementHost inside WinForms probably does something have to do with it. Have you tried to reproduce it in a standalone WPF project? – David Nelson Mar 30 '12 at 23:02
  • @Сергей Шулик I think that your dispather is busy with some other long-running operation. It would have the same result – surfen Mar 31 '12 at 17:02

4 Answers4

12

If you want something to run once an asynchronous operation is done, you should use it's Completed event.

See this answer for an example

var dispatcherOp = Dispatcher.BeginInvoke( /* your method here */);
dispatcherOp.Completed += (s, e) => { /* callback code here */ };

There's a chance the operation will complete before you subscribe, so you can test the Status property for completion after as well:

if (dispatcherOp.Status == DispatcherOperationStatus.Completed) { ... }

As for the actual problem, I'm not sure without being able to reproduce it. If I had to hazard a guess, it would be that your loop is tying up the current Dispatcher thread, so it can't process the BeginInvoke operation you told it to process, so it will always sit at Pending.

Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490
4

The status of your operation seems to be stale because enum is a value type and you store it in a variable. Changing your code to this should fix your problem :

    var op = Dispatcher.BeginInvoke(new Action(() => { ChangeCollectionByAction(action); }), null);
    while (op.Status!= DispatcherOperationStatus.Completed)
    {
        status = op.Wait(TimeSpan.FromSeconds(1));
    }

In fact you could replace all your code just by this single line:

Dispatcher.BeginInvoke(new Action(() => ChangeCollectionByAction(action))).Wait();
Mareek
  • 106
  • 1
  • 3
  • Your 2nd suggestion with Wait could be simplified with just `Dispatcher.Invoke` as Reed Copsey wrote in comments. – asktomsk Apr 03 '12 at 13:37
  • Your 1st suggestion seems to be the only way to call a WMI query from a call stack started by WindowsFormsHost and wait for the answer. 2nd suggestion doesn't actually work in that case, maybe it's not completely Async – Grant Jan 01 '14 at 13:18
1

Dispatcher.BeginInvoke() is not awaitable.

If .NET 4.5 is available for you, then you can use Dispatcher.InvokeAsync():

if (Dispatcher.Thread != Thread.CurrentThread)
    await Dispatcher.InvokeAsync(() => ChangeCollectionByAction(action));
Cœur
  • 37,241
  • 25
  • 195
  • 267
0

Can you check the Dispatcher.Thread.ThreadState value before call the BeginInvoke?

I think the problem that Dispatcher thread is Stopped. So calling Dispatcher.BeginInvoke in stopped thread will cause infinity Pending status because it can't run anything.

asktomsk
  • 2,289
  • 16
  • 23