Как я могу привязать свой интерфейс WP7 к данным, которые я получаю асинхронно? - PullRequest
2 голосов
/ 19 мая 2011

Я работаю над приложением для Windows Phone 7, которое позволяет пользователю просматривать статистику каждого из своих "сайтов" под своей учетной записью Chargify.com.

Я следил за Plural-sightучебное видео, которое помогло мне пройти большую часть пути, однако мои данные поступают из сложного источника, и они жестко закодировали список.

Итак, вот настройка:

Модель:

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

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

Проблема в том, что когда я вызываю this.DataContext - член _viewModel не делает 'У меня пока нет данных.Итак, представление привязывает данные, но значение пусто.

Есть предложения?Все работает хорошо, за исключением того, что представление не заполняет привязанные элементы управления данными ..

- Кори

Ответы [ 3 ]

1 голос
/ 20 мая 2011

Ваша проблема не в WPF, а в GetSiteStatistics. Поскольку вы получаете асинхронный результат, ваш метод почти всегда возвращает ноль, если только случайно BeginGetResponse не будет выполнен до возврата метода GetSiteStatistics. Это потерпит неудачу в любом приложении.

Вы можете заставить GetSiteStatistics всегда создавать и возвращать объект и заполнять его только в BeginGetResponse. Но тогда вы должны убедиться, что все это потокобезопасно.

1 голос
/ 19 мая 2011

_viewModel должно иметь ObservableCollection<> для статистики, и вы привязываете свой пользовательский интерфейс к этой коллекции. Всякий раз, когда элементы добавляются или удаляются из коллекции, пользовательский интерфейс обновляется автоматически (поскольку он отправляет событие OnPropertyChanged)

0 голосов
/ 27 мая 2011

Хорошо, я понял это, я думаю ..

Это было все время, я просто пропустил это.

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 (I 'Я не уверен, насколько это безопасно для потоков, и я действительно рекомендую вам использовать что-то вроде DispatcherHelper 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...