"Call flow bypass must be STA"

I know that there are several answers to this topic about SO, but I cannot get any of the solutions working for me. I am trying to open a new window from ICommand released from a dataset. Both of these parameters give the above error when creating a new window (in the "new MessageWindowP"):

Using TPL / FromCurrentSynchronizationContext Update: Works

public class ChatUserCommand : ICommand { public void Execute(object sender) { if (sender is UserC) { var user = (UserC)sender; var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(new Action<object>(CreateMessageWindow), user,CancellationToken.None, TaskCreationOptions.None,scheduler); } } private void CreateMessageWindow(object o) { var user = (UserC)o; var messageP = new MessageWindowP(); messageP.ViewModel.Participants.Add(user); messageP.View.Show(); } } 

Using ThreadStart: Update: not recommended, see Jon's answer

 public class ChatUserCommand : ICommand { public void Execute(object sender) { if (sender is UserC) { var user = (UserC)sender; var t = new ParameterizedThreadStart(CreateMessageWindow); var thread = new Thread(t); thread.SetApartmentState(ApartmentState.STA); thread.Start(sender); } } private void CreateMessageWindow(object o) { var user = (UserC)o; var messageP = new MessageWindowP(); messageP.ViewModel.Participants.Add(user); messageP.View.Show(); } } 

thank

EDIT. Based on the answers so far, I would like to point out that I also tried BeginInvoke in the current dispatcher, and also executed the code in the original method (how the code started). See below:

BeginInvoke Update: It is not recommended to see Jon's answer

 public class ChatUserCommand : ICommand { public void Execute(object sender) { if (sender is UserC) { var user = (UserC)sender; Dispatcher.CurrentDispatcher.BeginInvoke(new Action<object>(CreateMessageWindow), sender); } } private void CreateMessageWindow(object o) { var user = (UserC)o; var messageP = new MessageWindowP(); messageP.ViewModel.Participants.Add(user); messageP.View.Show(); } } 

In the same thread Update: works if you are already in the user interface thread

 public class ChatUserCommand : ICommand { public void Execute(object sender) { if (sender is UserC) { var user = (UserC)sender; var messageP = new MessageWindowP(); messageP.ViewModel.Participants.Add(user); messageP.View.Show(); } } } 

BeginInvoke using the link to the first / main window manager . Update: Works

  public void Execute(object sender) { if (sender is UserC) { var user = (UserC)sender; GeneralManager.MainDispatcher.BeginInvoke( DispatcherPriority.Normal, new Action(() => this.CreateMessageWindow(user))); } } 

where GeneralManager.MainDispatcher is a link to the First Window Dispatcher that I create:

  [somewhere far far away] mainP = new MainP(); MainDispatcher = mainP.View.Dispatcher; 

I'm at a loss.

+5
multithreading wpf task-parallel-library sta dispatcher
Apr 26 2018-12-12T00:
source share
3 answers

The calling stream must not only be an STA, but must also have a message loop. There is only one thread in your application that already has a message loop, and your main thread. Therefore, you should use Dispatcher.BeginInvoke to open a window from the main thread.

eg. if you have a link to the main application window ( MainWindow ), you can do

 MainWindow.BeginInvoke( DispatcherPriority.Normal, new Action(() => this.CreateMessageWindow(user))); 

Update: Be careful: you cannot blindly call Dispatcher.CurrentDispatcher because it does not do what you think it does. The documentation says that CurrentDispatcher :

Gets the dispatcher for the current thread and creates a new dispatcher if it is not already associated with the stream.

This is why you should use Dispatcher associated with an existing user interface control (for example, your main window, as in the example above).

+7
Apr 26 2018-12-12T00:
source share

With TPL, you can use StaTaskScheduler from additional features of TPL

It will run tasks on STA threads.

Used only for COM. Never tried to run multiple threads of the user interface.

+4
Apr 26 '12 at 15:00
source share

You are trying to create a window from a background thread. You cannot do this for various reasons. Usually you need to create a window in the main thread of the application.

For your case, a simple idea would simply do it immediately (just call CreateMessageWindow inside Execute ) instead of allocating Task , because your command will certainly be run from the main (UI) thread. If you are not sure about the thread in which your Execute is running, you can translate it into the user interface thread using Dispatcher.BeginInvoke() .

There are very few cases where you want your new window to start in a non-main theme. If this is really necessary in your case, you should add Dispatcher.Run(); after messageP.View.Show(); (using the second version of the code). This will start the message loop in the new thread.

You should not try to run a window in a TPL thread, because these threads are usually thread threads and therefore are not subject to control. For example, you cannot guarantee that they are STAs (usually an MTA).

Edit:
from the error in your updated code, it is clear that Execute running in some thread other than the UI. Try using Application.Current.Dispatcher instead of Dispatcher.CurrentDispatcher . ( CurrentDispatcher means the manager of the current thread, which may be incorrect if the current thread is not the main one.)

+3
Apr 26 2018-12-12T00:
source share



All Articles