3

this is my first post so forgive me if I make and mistakes.

I have a Task that returns a string. Within that Task I want to open a new window where the user enters a code. Once the code is entered and the window is closed the Task will return the code.

My code is as follows:

 public Task<string> GetLoginCode()
        {

            return Task.Run(() =>
            {
                CodeRequestView view = new CodeRequestView();
                CodeRequestViewModel viewModel = new CodeRequestViewModel();
                view.ShowDialog();
                return viewModel.Code;
            });

        }

The issue I'm having is when I run my project Im receiving a "The calling thread must be STA, because many UI components require this." exception at the constructor of the CodeRequestView.xmal.cs file.

Some help on how to resolve this would be greatly appreciated. Thanks!

TimoNZ
  • 49
  • 1
  • 4
  • Possible duplicate of [The calling thread must be STA, because many UI components require this in WPF](http://stackoverflow.com/questions/4183622/the-calling-thread-must-be-sta-because-many-ui-components-require-this-in-wpf) – Kylo Ren Jun 06 '16 at 08:12

2 Answers2

4

As you're calling UI stuff from a non-UI thread, you can use UI thread like Dispatcher.

  public Task<string> GetLoginCode()
            {

                return Task.Run(() =>
                {
                    CodeRequestViewModel viewModel = new CodeRequestViewModel();
                    Application.Current.Dispatcher.Invoke(delegate 
                    {
                        CodeRequestView view = new CodeRequestView();
                        view.ShowDialog();
                    });
                    return viewModel.Code;
                });
            }
J. Hasan
  • 492
  • 3
  • 14
1

WPF windows and controls need to run on an STA thread for its apartment state. Task.Run() uses the threadpool, which are defined as MTA, and they are not allowed to instantiate a Window subclass object which must be on STA thread.

You need to manually create a new thread, and set its apartment state to STA, before you use it to create new window objects. As you didn't show the full codes, so I have no idea why exactly does it need to return a Task<string> object instead of a string object. If you want this to run asynchronously (awaitable), your original implementation using Task.Run() will already run it on another thread (from the threadpool), and return to the caller immediately.

Anyway, this is an example:

public string GetLoginCode()
{
    string retVal;
    Thread viewThread = new Thread(() =>
    {
        CodeRequestView view = new CodeRequestView();
        CodeRequestViewModel viewModel = new CodeRequestViewModel();
        view.ShowDialog();
        retVal = viewModel.Code;
        });
    }
    viewThread.SetApartmentState(ApartmentState.STA);
    viewThread.Start();

    return retVal;
}

I've never tried this though, so I'm not sure if the return value will cause synchronization problems or not.

Jai
  • 8,165
  • 2
  • 21
  • 52
  • 1
    You might want to mention to the OP that having two STA threads, whilst technically feasible, is arguably not recommended for GUI apps. One must ask _why the need in the first place?_ –  Jun 06 '16 at 03:04
  • I do agree that many people tend to create new threads (or from threadpool) whenever possible, and it makes them feel that their application is more responsive. Most of the times the improvement is very marginal, but it gives them 10x the trouble that arises from having multiple threads. OP indeed needs to ask himself why this is needed. Remember, _the most elegant solution is one that is simple_. – Jai Jun 06 '16 at 03:16