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线程分离。

C#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内容上阻塞工作线程。