如何将我的WP7接口绑定到我异步检索的数据?

时间:2011-05-19 19:39:44

标签: c# windows-phone-7 asynchronous httpwebrequest

我正在使用Windows Phone 7应用,该应用允许用户查看其Chargify.com帐户下每个“网站”的统计信息。

我一直在关注多视点培训视频,这些视频让我大部分都在那里,但是我的数据来自一个复杂的来源,并且他们对列表进行了硬编码。

所以,这是设置:

模特:

 public SiteStats
 {
     public string seller_name { get; set;}
     public static GetSiteStatistics(string subdomain, string apiKey)
     {
        SiteStats retVal = null;
        HttpWebRequest request = WebRequest.Create(string.Format("https://{0}.chargify.com/stats.json", subdomain)) as HttpWebRequest;
        NetworkCredential credentials = new NetworkCredential(apiKey, "X");
        request.Credentials = credentials;
        request.Method = "GET";
        request.Accept = "application/json";
        request.BeginGetResponse(result =>
        {
            using (var response = request.EndGetResponse(result))
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    string stats = reader.ReadToEnd();
                    retVal = Json.Deserialize<SiteStats>(stats);
                }
            }
        }, request);
        return retVal;
     }
 }

ViewModel:

 public class SiteDetailViewModel : ViewModelBase
 {
    private SiteStats _siteStats;
    public SiteDetailViewModel(string subdomain) : this()
    {
       this._siteStats = SiteStats.GetSiteStatistics(subdomain, "apiKeyHere");
    }
    public SiteDetailViewModel : base() { ViewName = "site details"; }
    public SiteStats SiteStats
    {
      get { return _siteStats; }
      set {
        if (_siteStats != value) {
          _siteStats = value;
          OnPropertyChanged("SiteStats");
        }
     }
 }

观点:

 public partial class SiteDetailView : PhoneApplicationPage
 {
   private SiteDetailViewModel _viewModel;

   public SiteDetailView()
   {
      InitializeComponent();
      Loaded += new RoutedEventHandler(SiteDetailView_Loaded);
   }
   void SiteDetailView_Loaded(object sender, RoutedEventArgs e)
   {
      string subdomain = NavigationContext.QueryString["subdomain"];
      _viewModel = new SiteDetailViewModel(subdomain);
      this.DataContext = _viewModel;
   }
 }

问题是,当我调用this.DataContext时_viewModel成员还没有它的数据。因此,视图数据绑定 - 但值为空。

有什么建议吗?除了View没有将绑定控件填充到数据之外,一切正常。

- Kori

3 个答案:

答案 0 :(得分:1)

_viewModel应该有ObservableCollection<>个统计信息,并将UI绑定到该集合。每当在集合中添加或删除项目时,UI都会自动更新(因为它会发送OnPropertyChanged事件)

答案 1 :(得分:1)

您的问题不是WPF,而是GetSiteStatistics。由于您获得结果异步,因此您的方法几乎总是返回null,除非在GetSiteStatistics方法返回之前执行BeginGetResponse。它会在任何应用程序中失败。

您可以让GetSiteStatistics始终创建并返回一个对象,并仅在BeginGetResponse中填充它。但是你应该确保整个事情是线程安全的。

答案 2 :(得分:0)

好吧,我想到了这个想法......

一直都在那里我只是错过了它。

1)您异步获取数据。 “request.BeginGetResponse(result =&gt; ....);”在将来的某个时间发生,但是在发生这种情况之前你会返回结果..代码继续移动它不会等待你的结果。这是你想要做的:

public class SiteStats
{
  public string seller_name { get; set;}
  public static void GetSiteStatistics(string subdomain, string apiKey, Action<SiteStats> callback)
  {
    SiteStats retVal = null;
    HttpWebRequest request = WebRequest.Create(string.Format("https://{0}.chargify.com/stats.json", subdomain)) as HttpWebRequest;
    NetworkCredential credentials = new NetworkCredential(apiKey, "X");
    request.Credentials = credentials;
    request.Method = "GET";
    request.Accept = "application/json";
    request.BeginGetResponse(result =>
    {
        using (var response = request.EndGetResponse(result))
        {
            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                string stats = reader.ReadToEnd();
                retVal = Json.Deserialize<SiteStats>(stats);
                callback(retVal);
            }
        }
    }, request);
    //return retVal; // you can't return here
  }
}

相关的ViewModel代码如下所示:

public SiteDetailViewModel(string subdomain) : this()
{
   SiteStats.GetSiteStatistics(subdomain, "apiKeyHere", (result)=> {
     // Note you may need to wrap this in a Dispatcher call 
     // as you may be on the wrong thread to update the UI 
     // if that happens you'll get a cross thread access 
     // you will have to expose the dispatcher through some 
     // other mechanism. One way to do that would be a static
     // on your application class which we'll emulate and 
     // I'll give you the code in a sec
     myRootNamespace.App.Dispatcher.BeginInvoke(()=>this._siteStats = results);
   });
}

以下是您需要对Application类进行的更改(我不确定这是什么线程安全,我真的建议您使用类似MVVMLight的DispatcherHelper。

public partial class App : Application
{

    public static Dispatcher Dispatcher { get; private set; } // Add this line!!

    // More code follows we're skipping it

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        this.RootVisual = new MainPage(); 
        Dispatcher = this.RootVisual.Dispatcher; // only add this line!!
    }

    private void Application_Exit(object sender, EventArgs e)
    {
        // Do this to clean up
        Dispatcher = null;
    }
}