C#GUI线程错误
本文关键字:错误 线程 C#GUI | 更新日期: 2023-09-27 18:27:13
我正在开发一个应用程序,该应用程序应该通过套接字接口接收命令,然后在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();
}
}
问题是,当我craete一个新的PopupType1对象时,我会得到以下异常:
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。来自后台工作人员的回调是您访问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内容上阻塞工作线程。