如何将我的 WP7 接口绑定到我正在异步检索的数据
本文关键字:异步 检索 数据 我的 WP7 绑定 接口 | 更新日期: 2023-09-27 17:56:53
我正在开发一个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;
}
}
视图模型:
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;
}
}
问题是,当我打电话给这个时。数据上下文 - _viewModel成员还没有数据。因此,视图数据绑定 - 但值为空。
有什么建议吗?一切正常,除了视图没有将绑定控件填充到数据中。
- 科里
_viewModel
应该有一个统计信息ObservableCollection<>
,并将UI绑定到该集合。每当在集合中添加或删除项时,UI 都会自动更新(因为它会发出 OnPropertyChanged
事件)
你的问题不是WPF,而是GetSiteStatistics。由于您获得的结果是异步的,因此您的方法几乎总是返回 null,除非碰巧在 GetSiteStatistics 方法返回之前执行 BeginGetResponse。它在任何应用程序中都会失败。
您可以让 GetSiteStatistics 始终创建并返回一个对象,并且只在 BeginGetResponse 中填写它。但是你应该确保整个事情是线程安全的。
..
它一直在那里,我只是错过了它。
1)您正在异步获取数据。"请求。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
}
}
相关的视图模型代码将如下所示:
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);
});
}
以下是您需要对应用程序类进行的更改(我不确定这有多线程安全,我真的建议您使用 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;
}
}