2012-03-09 101 views
3

我正在開發一個應用程序,該應用程序應該通過套接字接口接收命令,然後在GUI中執行它們。此應用程序正在C#.NET 4.0中開發,它使用WPF作爲其GUI。C#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處理。

回答

3

您還需要在UI線程上創建UI控件。所以基本上所有的ProcessPopup需要在UI線程上,你的情況來執行,而不只是mainScreen.ShowPopup()

1

如果PopupType包含任何控件(和它看起來像它一樣),它應該是主要的GUI線程上創建。

2

你是創建後臺線程彈出。
這將無法正常工作;你只能在UI線程上創建控件。

您應該將昂貴(慢)的邏輯從UI類中分離出來,並在後臺線程上單獨執行。

0

通過設計,只有創建UI對象(主)的線程才能訪問UI對象。從你的描述中,「套接字接口有一個工作者線程」,這樣接口就無法訪問UI。來自背景工作者的回調是您可以訪問UI的位置。既然你想保持聽衆熱,我認爲「進步」可能更好地訪問用戶界面。

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,而不是調用的。這是爲了確保您的UI更新異步,以便您不必在某些UI上阻止您的工作線程。