我正在使用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
答案 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;
}
}