Metro c ++异步编程和UI更新。我的技术?

时间:2012-09-24 23:58:55

标签: xaml visual-c++ windows-8 microsoft-metro windows-store

问题:当我想渲染异步检索的传入数据时,我崩溃了。

应用程序启动并使用XAML显示一些对话框。一旦用户填写他们的数据并单击登录按钮,XAML类就会有一个工作者类的实例,它为我执行HTTP操作(使用IXMLHTTPRequest2异步)。当应用程序成功登录到Web服务器时,我的.then()块会触发,我会对我的主xaml类进行回调以对资产进行一些渲染。

我总是在委托中遇到崩溃(主要的XAML类),这让我相信我不能使用这种方法(纯虚拟类和回调)来更新我的UI。我想我无意中试图从错误的线程做一些非法的事情,这是异步调用的副产品。

是否有更好或不同的方式我应该通知主XAML类它是时候更新它的UI了?我来自iOS世界,我可以使用NotificationCenter。

现在,我看到微软在这里有自己的委托类型:http://msdn.microsoft.com/en-us/library/windows/apps/hh755798.aspx

你认为如果我使用这种方法而不是我自己的回调它会不再崩溃吗?

如果您需要更多澄清或不需要,请告诉我。

这是代码的主旨:     公共接口类ISmileServiceEvents     {     public://必需的方法         虚拟void UpdateUI(bool isValid)abstract;     };

// In main XAML.cpp which inherits from an ISmileServiceEvents
void buttonClick(...){
    _myUser->LoginAndGetAssets(txtEmail->Text, txtPass->Password);
}
void UpdateUI(String^ data) // implements ISmileServiceEvents
{
    // This is where I would render my assets if I could.
    // Cannot legally do much here. Always crashes. 
    // Follow the rest of the code to get here. 
}

// In MyUser.cpp
void LoginAndGetAssets(String^ email, String^ password){
   Uri^ uri = ref new URI(MY_SERVER + "login.json");
   String^ inJSON = "some json input data here"; // serialized email and password with other data

   // make the HTTP request to login, then notify XAML that it has data to render.
   _myService->HTTPPostAsync(uri, json).then([](String^ outputJson){
      String^ assets = MyParser::Parse(outputJSON);
      // The Login has returned and we have our json output data 
      if(_delegate)
      {
         _delegate->UpdateUI(assets);
      }
   });
}

// In MyService.cpp
task<String^> MyService::HTTPPostAsync(Uri^ uri, String^ json)
{
    return _httpRequest.PostAsync(uri, 
        json->Data(),
        _cancellationTokenSource.get_token()).then([this](task<std::wstring> response)
    {
        try
        {
           if(_httpRequest.GetStatusCode() != 200) SM_LOG_WARNING("Status code=", _httpRequest.GetStatusCode());
               String^ j = ref new String(response.get().c_str());
               return j;
        }
        catch (Exception^ ex) .......;
    return ref new String(L"");
    }, task_continuation_context::use_current());
}

编辑:BTW,我在更新UI时得到的错误是: “将无效参数传递给认为无效参数致命的函数。” 在这种情况下,我只是试图在我的回调中执行

txtBox->Text = data;

2 个答案:

答案 0 :(得分:0)

您似乎正在从错误的上下文更新UI线程。您可以使用task_continuation_context::use_arbitrary()来更新UI。请参阅this document中的“控制执行线程”示例(编组的讨论位于底部)。

答案 1 :(得分:0)

因此,事实证明,当你有一个延续时,如果你没有在lambda函数之后指定一个上下文,那么它默认为use_arbitrary()。这与我在MS视频中学到的内容相矛盾。

然而,通过将use_currrent()添加到与GUI有关的所有.then块,我的错误消失了,一切都能够正确呈现。

我的GUI调用生成一些任务的服务,然后调用也执行异步操作的HTTP类。回到HTTP类,我使用use_arbitrary(),以便它可以在辅助线程上运行。这很好用。只要确保在与GUI有关的任何事情上使用use_current()。

现在您有了我的答案,如果查看原始代码,您会看到它已经包含use_current()。这是事实,但为了简化示例,我遗漏了一个包装函数。这是我需要添加use_current()的地方。