C#GUI线程错误

我正在开发一个应该通过套接字接口接收命令然后在GUI中执行它们的应用程序。 此应用程序正在C#.NET 4.0中开发,它使用WPF作为其GUI。

套接字接口有一个工作线程,它继续监听套接字并处理其命令,因此,如果收到Show Popup命令,则工作线程调用一个管理器类,负责创建弹出窗口并在主屏幕上显示它。

创建弹出窗口然后调用主屏幕的管理器方法如下:

public void ProcessPopup(PopupModel model) { switch (model.ScreenType) { case Screens.Type1: popup = new PopupType1(); break; case Screens.Type2: popup = new PopupType2(); break; case Screens.Type3: popup = new PopupType3(); break; case Screens.Type4: popup = new PopupType4(); break; } viewModel.SetModel(model); if (!Dispatcher.CurrentDispatcher.Equals(App.Current.Dispatcher)) { App.Current.Dispatcher.Invoke((ThreadStart)delegate { mainScreen.ShowPopup(popup); }); } else { mainScreen.ShowPopup(popup); } } 

PopupType1类是:

 public partial class PopupType1 : UserControl { public PopupType1 () { InitializeComponent(); } } 

问题是,当我创建一个新的PopupType1对象时,我得到以下exception:

 System.InvalidOperationException: The calling thread must be STA, because many UI components require this. at System.Windows.Input.InputManager..ctor() at System.Windows.Input.InputManager.GetCurrentInputManagerImpl() at System.Windows.Input.InputManager.get_Current() at System.Windows.Input.KeyboardNavigation..ctor() at System.Windows.FrameworkElement.FrameworkServices..ctor() at System.Windows.FrameworkElement.EnsureFrameworkServices() at System.Windows.FrameworkElement..ctor() at System.Windows.Controls.Control..ctor() at System.Windows.Controls.UserControl..ctor() at MyApp.Views.PopupType1..ctor() at MyApp.Manager.ProcessPopup(PopupModel model) at MyApp.CommunicationController.ProcessAsync(XDocument messageXml) 

我已经尝试了几件事情,比如将我的工作线程转换为STA线程,或创建一个新的STA线程来处理Popup的创建,但是它们引起的问题比他们解决的要多。

最后,重要的是要提到我正在这样做,因为我的应用程序在其操作期间经历了几次“冻结”,并且我认为它们与WPF GUI线程太过于无法完成任务以便正确响应这一事实有关,因此我试图将非GUI处理与GUI线程分开。

您还需要在UI线程上创建UI控件。 所以基本上所有的ProcessPopup需要在你的情况下在UI线程上执行,而不仅仅是mainScreen.ShowPopup ()

您正在后台线程上创建弹出窗口。
那不行; 您只能在UI线程上创建控件。

您应该将昂贵(慢)逻辑与UI类分开,并在后台线程上单独执行。

如果PopupType包含任何控件(它看起来像它),它应该在主GUI线程上创建。

通过设计,只有创建UI对象(main)的线程才能访问UI对象。 根据您的描述“套接字接口有一个工作线程”,这样接口就无法访问UI。 来自backgroundworker的回调是您获得UI访问权限的地方。 由于您希望保持监听器的热度,我认为“进度”可能更适合访问UI。

您应该在UI线程中创建UI元素。 您可以执行以下操作,例如:

  private static UserControl CreatePopup(PopupModel model){ switch (model.ScreenType) { case Screens.Type1: popup = new PopupType1(); break; case Screens.Type2: popup = new PopupType2(); break; case Screens.Type3: popup = new PopupType3(); break; case Screens.Type4: popup = new PopupType4(); break; } } 

并将ProcessPopup更改为:

 public void ProcessPopup(PopupModel model) { viewModel.SetModel(model); App.Current.Dispatcher.BeginInvoke( new Action(()=>{ mainScreen.ShowPopup(CreatePopup(model)); } )); } 

另外,请注意使用BeginInvoke而不是Invoke。 这是为了确保您的UI异步更新,这样您就不必在某些UI上阻止您的工作线程。