2012-07-23 48 views
0

我在PRISM體系結構中有一個WPF應用程序。WPF + PRISM - 不能'LoadModule'在單獨的線程

我有一個「登錄視圖」,當應用程序加載時顯示在「主要區域」中。

當用戶按'登錄' - 我連接到WCF服務,驗證用戶,並從該服務獲取該用戶的角色列表。

然後 - 根據用戶的角色 - 使用「模塊管理器」加載不同的模塊。

問題是 - 我想要在「登錄」按鈕在單獨的線程中完成後的所有工作,因爲它可能需要時間來連接到服務等,我不希望UI是凍結。

但是 - 如果我把代碼「連接,進行身份驗證,得到角色,加載模塊」在一個單獨的線程 - 我得到一個異常時,我稱之爲「_moduleManager.LoadModule」,上面寫着:

主叫線程必須是STA,因爲許多UI組件都需要這個。

我該如何解決這個問題?

我已經嘗試過不同的解決方案。 我試圖設置新線程的'Apartment State = STA',但它沒有幫助。 我想過在View-Model的構造函數中保存'Dispatcher'對象,然後在調用'LoadModule'時執行'dispatcher.Invoke',但這是糟糕的設計(View-Model不應該使用Dispatcher,這對測試不利)。

任何想法我可以解決這個問題?

只有'LoadModule'讓我感到悲傷,所有其他的東西工作正常。

[更新] - 增加了代碼示例:

[Export] 
public class LoginViewModel : NotificationObject 
{ 
    [ImportingConstructor] 
    public LoginViewModel(IRegionManager regionManager, IModuleManager moduleManager) 
    { 
     this.LoginCommand = new DelegateCommand(LoginExecute, LoginCanExecute); 
     this._regionManager = regionManager; 
     this._moduleManager = moduleManager; 
    } 

    private void LoginExecute() 
    { 
     IsBusy = true; // Set this to 'true' so controls go disabled 
     LoginStatus = ""; // Clear the 'login status' string 


     Thread loginThread = new Thread(new ThreadStart(LoginWork)); 
     loginThread.SetApartmentState(ApartmentState.STA); 
     loginThread.Start(); 
    } 

    private void LoginWork() 
    { 
     ParamsToGetRoles param = new ParamsToGetRoles 
     { 
      Username = Username, 
      InputtedPassword = Password 
     }; 

     try 
     { 
      // Connect to the secure service, and request the user's roles 
      _clientSecure = new AuthenticationServiceClient("WSHttpBinding_MyService"); 
      _clientSecure.ClientCredentials.UserName.UserName = param.Username; 
      _clientSecure.ClientCredentials.UserName.Password = param.InputtedPassword; 
      _clientSecure.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted); 
      var local = _clientSecure.ChannelFactory.CreateChannel(); 
      _clientSecure.GetRolesCompleted += new EventHandler<GetRolesCompletedEventArgs>(clientSecure_GetRolesCompleted); 
      _clientSecure.GetRolesAsync(param); 
     } 
     catch (Exception ex) 
     { 
     Console.WriteLine("Exception : " + ex.Message.ToString()); 
     } 
    } 

    void clientSecure_GetRolesCompleted(object sender, GetRolesCompletedEventArgs e) 
    { 
     if (e.Error == null) 
     { 
      _clientSecure.Close(); 
      LoginSuccess(e.Result.UserRoles); 
     } 
     else 
     { 
      LoginFailure("Unable to authenticate"); 
     } 
     _clientSecure = null; 
    } 

    private void LoginSuccess(List<UserTypeEnum> rolesOfAuthenticatedUser) 
    { 
     LoginStatus = "Success"; 

     if (rolesOfAuthenticatedUser.Contains(UserTypeEnum.Administrator)) 
     { 
      // This is what throws the exception ! 
      // This is called by the 'EndInvoke' of the 'GetRoles' operation, 
      // Which was called in the 'LoginWork' function which was run on a separate thread ! 
      _moduleManager.LoadModule(WellKnownModuleNames.ModuleAdmin); 
     } 

     NavigateToMainMenu(); 

     this.IsBusy = false; 
    } 
} 
+1

你確定你把正確的線程的狀態設置爲STA?同時,http://stackoverflow.com/questions/2329978/the-calling-thread-must-be-sta-because-many-ui-components-require-this和其他許多人問完全相同的 – stijn 2012-07-23 10:21:44

+1

我只是用'Dispatcher'發送LoadModules命令到主UI線程:) – Rachel 2012-07-23 13:14:19

+0

@Rachel - 但不使用'Dispatcher'對測試目的不利?還有 - 我認爲'視圖模型'應該對UI一無所知,並且如果我使用'Dispatcher'在UI線程上做某些事情 - 這有點違背目的,不是嗎? – 2012-07-24 05:40:39

回答

2

你應該附加調試和設定在clientSecure_GetRolesCompleted斷點檢查線程窗口。我很確定它沒有被loginThread調用:LoginWork確實在loginThread中運行,然後它將一個事件處理程序添加到異步操作的完成事件中。 Async =在另一個線程中運行。

那麼,什麼可能發生的情況:

  • LoginExecute執行在UI線程
  • 啓動一個單獨的線程B運行LoginWork
  • 電話GetRolesAsync所以啓動一個線程C(這是 STA)獲得角色
  • 線程C最終調用'clientSecure_GetRolesCompleted',而不是線程B

因此,您不需要爲LoginWork設置單獨的線程,因爲實際工作已經作爲異步操作完成。爲了解決加載問題,可以嘗試使'獲取角色'線程STA或更好地使用調度器,以便在UI線程上調用LoginSuccess

+0

優秀的方式把它。調度員工作。只是澄清 - 我確實需要一個單獨的'LoginWork'線程,因爲連接到WCF服務可能需要1到2秒,我不希望它掛在UI線程,所以這就是爲什麼我把'LoginWork'放在一個不同的線程。 – 2012-07-24 11:31:30

+0

這就是說 - 現在使用'ASYNC'調用模式似乎很愚蠢,因爲它不是在UI線程上完成的。 所以我乾脆叫SYNC操作和完成時間通過調度元帥「的LoadModule」 ......在這種情況下 – 2012-07-24 11:32:36

+0

,是的,那是最好的選擇。 – stijn 2012-07-24 12:56:25