2010-10-02 81 views
6

我今天一直在試驗WP7應用程序,並且遇到了一些困難。 我喜歡在用戶界面和主應用程序代碼之間分開,但是我撞到了牆上。WP7中的異步調用

我成功實現了一個webclient請求並得到了一個結果,但因爲調用是異步的,我不知道如何將這個備份傳遞給UI級別。我似乎無法等待響應完成或任何事情。 我一定在做錯事。

(這是xbox360Voice庫,我有下載在我的網站:http://www.jamesstuddart.co.uk/Projects/ASP.Net/Xbox_Feeds/這我移植到WP7作爲測試)

這裏是後端代碼片段:

internal const string BaseUrlFormat = "http://www.360voice.com/api/gamertag-profile.asp?tag={0}"; 
    internal static string ResponseXml { get; set; } 
    internal static WebClient Client = new WebClient(); 

    public static XboxGamer? GetGamer(string gamerTag) 
    { 
     var url = string.Format(BaseUrlFormat, gamerTag); 

     var response = GetResponse(url, null, null); 

     return SerializeResponse(response); 
    } 

    internal static XboxGamer? SerializeResponse(string response) 
    { 
     if (string.IsNullOrEmpty(response)) 
     { 
      return null; 
     } 

     var tempGamer = new XboxGamer(); 
     var gamer = (XboxGamer)SerializationMethods.Deserialize(tempGamer, response); 

     return gamer; 
    } 

    internal static string GetResponse(string url, string userName, string password) 
    { 


      if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) 
      { 
       Client.Credentials = new NetworkCredential(userName, password); 
      } 

      try 
      { 
       Client.DownloadStringCompleted += ClientDownloadStringCompleted; 
       Client.DownloadStringAsync(new Uri(url)); 

       return ResponseXml; 
      } 
      catch (Exception ex) 
      { 
       return null; 
      } 
     } 



    internal static void ClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) 
    { 
     if (e.Error == null) 
     { 
      ResponseXml = e.Result; 
     } 
    } 

這是前端代碼:

public void GetGamerDetails() 
{ 
    var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r"); 
    var xboxGamer = xboxManager.GetGamer(); 

    if (xboxGamer.HasValue) 
    { 
     var profile = xboxGamer.Value.Profile[0]; 
     imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl)); 
     txtUserName.Text = profile.GamerTag; 
     txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000"); 
     txtZone.Text = profile.PlayerZone; 
    } 
    else 
    { 
     txtUserName.Text = "Failed to load data"; 
    } 
} 

現在我明白我需要把東西ClientDownloadStringCompleted,但我不確定是什麼。

回答

6

您遇到的問題是隻要將異步操作引入到代碼路徑中,整個代碼路徑就需要變爲異步。

  • 因爲GetResponse電話DownloadStringAsync它必須成爲異步的,它不能返回一個字符串,它只能做到在回調
  • 因爲GetGamer電話GetResponse現在是異步的,不能返回一個XboxGamer,它只能在回調中做到這一點
  • 因爲GetGamerDetails調用GetGamer現在它是異步的,它不能繼續其調用後的代碼,它只能在它從GetGamer收到回調後才能做到。
  • 因爲GetGamerDetails現在是異步的,所以調用它也必須承認這種行爲。
  • ....這一直延續到用戶事件發生的鏈條頂端。

這裏有一些空氣代碼敲入代碼的一些異步性。

public static void GetGamer(string gamerTag, Action<XboxGamer?> completed) 
{ 
    var url = string.Format(BaseUrlFormat, gamerTag); 

    var response = GetResponse(url, null, null, (response) => 
    { 
     completed(SerializeResponse(response)); 
    }); 
} 


internal static string GetResponse(string url, string userName, string password, Action<string> completed)  
{  

    WebClient client = new WebClient(); 
    if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))  
    {  
     client.Credentials = new NetworkCredential(userName, password);  
    }  

    try  
    {  
     client.DownloadStringCompleted += (s, args) => 
     { 
      // Messy error handling needed here, out of scope 
      completed(args.Result); 
     }; 
     client.DownloadStringAsync(new Uri(url));   
    }  
    catch  
    {  
     completed(null);  
    }  
}  


public void GetGamerDetails()    
{    
    var xboxManager = XboxFactory.GetXboxManager("DarkV1p3r");    
    xboxManager.GetGamer((xboxGamer) =>    
    { 
     // Need to move to the main UI thread. 
     Dispatcher.BeginInvoke(new Action<XboxGamer?>(DisplayGamerDetails), xboxGamer); 
    }); 

} 

void DisplayGamerDetails(XboxGamer? xboxGamer) 
{ 
    if (xboxGamer.HasValue)    
    {    
     var profile = xboxGamer.Value.Profile[0];    
     imgAvatar.Source = new BitmapImage(new Uri(profile.ProfilePictureMiniUrl));    
     txtUserName.Text = profile.GamerTag;    
     txtGamerScore.Text = int.Parse(profile.GamerScore).ToString("G 0,000");    
     txtZone.Text = profile.PlayerZone;    
    }    
    else    
    {    
     txtUserName.Text = "Failed to load data";    
    }   
} 

正如你可以看到異步編程可以得到真的凌亂。

+0

「正如你所看到的,異步編程可能會變得雜亂無章。」 ...這是使用F#的一個原因。藉助F#,您可以將代碼重構爲異步,而無需執行所有的回調 - 反轉控制翻轉。 – Brian 2010-10-02 20:29:24

+0

@Brian:我完全同意。我只是希望函數式編程能夠變得更容易理解,這可能是年輕人的頭腦,但不管我多麼努力,我都無法理解我的概念。 – AnthonyWJones 2010-10-02 20:57:06

+0

生病給了這個去一個明天,謝謝 – JamesStuddart 2010-10-03 00:25:07

2

您一般有2個選項。要麼您將後端代碼作爲異步API公開,要麼您需要等待GetResponse中的呼叫完成。

這樣做的異步方式意味着啓動一個地方的過程,然後返回,並在數據可用時更新UI。這通常是首選方法,因爲只要方法正在運行,在UI線程上調用阻塞方法將使您的應用程序看起來沒有響應。

+0

你有沒有這方面的例子,因爲我明白'我該怎麼做',但我不知道如何實現它。我還沒有做任何SL之前,所以我在一個陡峭的學習曲線。 – JamesStuddart 2010-10-02 14:54:13

1

我認爲「Silverlight Way」將使用databinding。您的XboxGamer對象應該實現INotifyPropertyChanged接口。當你調用GetGamer()時,它會立即返回一個「空的」XboxGamer對象(可能帶有GamerTag ==「Loading ...」或其他東西)。在您的ClientDownloadStringCompleted處理程序中,您應該反序列化返回的XML,然後觸發INotifyPropertyChanged.PropertyChanged事件。

如果您查看SDK中的「Windows Phone Databound Application」項目模板,則ItemViewModel類以此方式實現。

+0

謝謝,這看起來像我可能要找的東西。我看過模板(就像在'新項目'窗口中看到的一樣,但沒有創建一個)。 – JamesStuddart 2010-10-02 18:09:40

0

Here是如何將異步功能暴露給WP7上的任何類型。