C# GUI线程错误

3
我正在开发一个应用程序,它应该通过套接字接口接收命令,然后在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对象时,会出现以下异常:
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线程分离。

5个回答

3

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


2

您正在后台线程上创建弹出窗口。
这样做是行不通的;您只能在UI线程上创建控件。

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


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

0
你应该在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操作上阻塞工作线程。

0

按设计,只有创建 UI 对象的线程(主线程)才能访问该对象。从您的描述中可以看出,“套接字接口有一个工作线程”,因此该接口无法访问 UI。通过后台工作器的回调函数,您可以访问 UI。由于您希望保持监听器处于活动状态,我认为“进度”可能更适合访问 UI。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接