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

发布于 2024-11-08 16:52:03 字数 2371 浏览 0 评论 0原文

我正在开发一个 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 成员还没有数据。因此,视图数据绑定 - 但该值为空。

有什么建议吗?一切工作正常,除了视图没有填充数据的绑定控件..

-- Kori

I'm working on a Windows Phone 7 app which allows a user to view the statistics of each of their "sites" under their Chargify.com account.

I've been following a Plural-sight training video which got me most of the way there, however my data is coming from a complex source and they had hard-coded a list.

So, here's the setup:

The model:

 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;
     }
 }

The 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");
        }
     }
 }

The View:

 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;
   }
 }

The problem is, that when I call this.DataContext - the _viewModel member doesn't have it's data yet. So, the view databinds - but the value is empty.

Any suggestions? Everything works fine except that the View isn't populating the bound controls to the data ..

-- Kori

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

忘东忘西忘不掉你 2024-11-15 16:52:03

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

_viewModel should have an ObservableCollection<> for the stats and you bind your UI to that collection. Whenever items are added to or removed from the collection, the UI is updated automatically (since it sends out the OnPropertyChanged event)

木森分化 2024-11-15 16:52:03

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

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

Your problem is not WPF, but GetSiteStatistics. Since you're getting the result async your method almost always returns null, unless by chance the BeginGetResponse is executed before the GetSiteStatistics method returns. It would fail in any application.

You could have GetSiteStatistics always create and return an object and only fill it in BeginGetResponse. But then you should make sure the whole thing is thread safe.

单挑你×的.吻 2024-11-15 16:52:03

好吧,我想我已经弄清楚了。

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

1)您正在异步获取数据。 “request.BeginGetResponse(result=> .... );”将来的某个时候发生,但您在发生之前返回结果。代码不断移动,它不会等待您的结果。这就是您想要做的:

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 的东西调度助手。

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;
    }
}

Alright I got this one figured out I think..

It was there all along I just missed it.

1) you are asynchronously getting data. The "request.BeginGetResponse(result=> .... );" happens sometime in the future, but you return your result before that happens.. the code keeps moving it doesn't wait for your result. Here's what you want to do:

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
  }
}

The pertinent ViewModel Code will look something like this:

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);
   });
}

Here are the changes you need to make to the Application class (I'm not sure how threadsafe this is and I would really recommend that you use something like MVVMLight's 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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文