C#GUI线程错误

时间:2012-03-09 18:40:22

标签: c# wpf .net-4.0

我正在开发一个应该通过套接字接口接收命令然后在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 个答案:

答案 0 :(得分:3)

您还需要在UI线程上创建UI控件。因此,基本上所有ProcessPopup都需要在您的案例中的UI线程上执行,而不仅仅是mainScreen.ShowPopup()

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

答案 4 :(得分: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上阻止您的工作线程。