0

I wanted to open a new WPF window from my MainWindow's ViewModel with a 2 second delay by doing the following:

await Task.Delay(2000).ContinueWith(_ =>
                    {
                        Restart wndRestart = new Restart();
                        wndRestart.Show();
                    }
                );

Sadly, I am constantly getting: System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.'

I actually looked up this, but I was unable to use [STAThread] even after refactoring a method.

Jishan
  • 1,654
  • 4
  • 28
  • 62
  • 2
    Voting to reopen, as the linked answer doesn't really address _this_ particular problem and the required solution/workaround. – Joey Sep 13 '20 at 19:05
  • _"the linked answer doesn't really address this particular problem"_ -- sure it does. Or at least, it addresses _an example of this vaguely stated problem_. The fact is, there's not enough detail in the question to know what's wrong. If the code posted were executed in the UI thread in the first place, the exception wouldn't happen at all. So one way to fix it is to execute it in the UI thread, per the duplicate. There are lots of other scenarios that could result in the exception, but there's zero evidence in the question any of those apply. – Peter Duniho Sep 13 '20 at 19:11
  • 1
    Don't use `Task.ContinueWith`. Simply continue with your code after the `await`. – BionicCode Sep 13 '20 at 19:25
  • Only to clarify this: a `Window` can only be created on a UI thread. – BionicCode Sep 13 '20 at 19:27

1 Answers1

1

You can pass your own synchronization context to ContinueWith to tell the task scheduler where your continuation should run:

await Task.Delay(2000).ContinueWith(_ =>
                {
                    Restart wndRestart = new Restart();
                    wndRestart.Show();
                },
                TaskScheduler.FromCurrentSynchronizationContext()
            );

Otherwise you'll get a thread-pool thread that most likely isn't STA.

However, for a one-shot in WPF to perform some action after a delay, it's perfectly fine to just use a DispatcherTimer as well.

Joey
  • 344,408
  • 85
  • 689
  • 683
  • 2
    Or simply do not use `Task.ContinueWith` in this context. Since you await `Task.Delay` and the `Task` is configured with `ConfigureAwait(true)` by default, the execution would continue on the proper thread (UI thread). – BionicCode Sep 13 '20 at 19:23
  • That's an option, if targeting .NET 4.5 and later, yes. I've once stumbled over the very same problem, just tied to .NET 4.0 ... – Joey Sep 13 '20 at 19:34
  • 2
    Basically your are right, but `Task.Delay` wasn't available in .NET 4.0. `await` was also not available in this version. This means the questions targets .NET >= 4.5. There is no sense to continue execution after a simple `await` using `Task.ContinueWith`, except you want to execute the continuation on a worker thread - which is clearly not the case in the OP's scenario. So it appears to be wiser to drop the overhead of configuring the continuation's thread pool thread. Note that `Task.Delay` is a pure asynchronous method, that doesn't run on a thread pool thread. – BionicCode Sep 13 '20 at 20:18
  • Good point. Agreed :-) – Joey Sep 14 '20 at 13:18