我正在开发一个应该通过套接字接口接收命令然后在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线程分开。
答案 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上阻止您的工作线程。