首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >WPF + PRISM -不能在单独线程中“LoadModule”

WPF + PRISM -不能在单独线程中“LoadModule”
EN

Stack Overflow用户
提问于 2012-07-23 09:46:18
回答 1查看 1.5K关注 0票数 0

我在PRISM架构中有一个WPF应用程序。

我有一个‘登录视图’,显示在‘主区域’时,应用程序加载。

当用户按下'Login‘-我连接到WCF服务时,对用户进行身份验证,并从服务中获取该用户的角色列表。

然后--根据用户的角色--使用‘Manager’加载不同的模块。

问题是--我希望在按下“Login”按钮后的所有工作都在一个单独的线程中完成,因为连接到服务等可能需要时间,而且我不希望UI被冻结。

但是-如果我将代码放在单独的线程中以“连接、身份验证、获取角色、加载模块”--当我调用“_moduleManager.LoadModule”时会得到一个异常,它说:

调用线程必须是STA,因为许多UI组件都需要这个.

我怎么才能解决这个问题?

我尝试过不同的解决方案。我试着设置新线程的“公寓状态=STA”,但没有帮助。我考虑将'Dispatcher‘对象保存在View的构造函数中,然后在调用'LoadModule’时执行'dispatcher.Invoke‘,但这是错误的设计(View不应该使用Dispatcher,而且对测试也不好)。

我有什么办法解决这个问题吗?

只有'LoadModule‘让我感到悲伤,所有其他东西都很好用。

更新-添加代码示例:

代码语言:javascript
复制
[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;
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-07-24 08:40:28

您应该附加调试器并检查在clientSecure_GetRolesCompleted设置了断点的线程窗口。我很确定它不是从loginThread调用的:虽然LoginWork确实在loginThread中运行,但是它为异步操作的完成事件添加了一个均衡器。异步=在另一个线程中运行。

那么可能会发生什么:

  • LoginExecute在UI线程中执行。
  • 启动独立线程B以运行LoginWork
  • 调用GetRolesAsync,所以启动一个线程C(不是STA)来获取角色
  • 线程C最终调用'clientSecure_GetRolesCompleted',而不是调用线程B。

因此,您不需要LoginWork单独的线程,因为实际的工作已经作为异步操作完成了。为了避免加载问题,要么尝试使'get角色‘线程STA,或者更好地使用dispatcher,以便在UI线程上调用LoginSuccess

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11610083

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档