Developpement Windows Phone - partie 23

Consommer un service Windows Azure Data avec OData

Cet article fait partie d’une série d’articles sur le développement Windows Phone. Il s’agit d’une traduction des articles se trouvant sur la MSDN.

Sommaire

Bien débuter et fondamentaux

Visuels et média

Travailler avec les données

Sondes et autres fonctionnalités spécifiques au téléphone


Consommer un service Windows Azure Data avec OData

Le protocole Open Data (OData) est basé sur une entité et un modèle de relation qui vous permet d'accéder à des données REST. OData autorise l'utilisation protocole standard HTTP pour exécuter des requêtes, et même créer, mettre à jour et supprimer des données depuis un service de données. En utilisant la bibliothèque de client OData pour Windows Phone, votre application peut consommer des données d'un service Windows Azure ou n'importe quel autre service qui supporte le protocole OData. La bibliothèque cliente génère des requêtes HTTP vers un service OData et transforme les données dans le flux de réponse en objets sur le client.

Remarque

Le client OData pour Windows Phone 7 est disponible en tant que bibliothèque séparée, que vous pouvez télécharger et installer depuis la page Open Data Protocol—client libraries.

Ce tutoriel contient les sections suivantes:

Les exemples de ce tutoriel utilisent Silverlight dans un navigateur pour simuler le comportement sur Windows Phone. Le résultat peut être sensiblement différent sur un périphérique Windows Phone.

Survol des services OData pour Windows Phone

Les applications pour périphériques mobiles se basent énormément sur des sources de données distantes, et la plateforme Windows Azure fournit une excellente source de données pour vos applications Windows Phone. OData est l'un des principaux mécanismes d'accès aux données de la plateforme Windows Azure. Plusieurs composants de la plateforme Windows Azure fournissent des flux OData, incluant Windows Azure Table Storage, Microsoft SQL Azure, et Windows Azure Marketplace DataMarket. Une liste des flux OData est disponible sur site Web du protocole Open Data.

Vous pouvez aussi utiliser des services de données WCF pour exposer vos propres données en tant que flux OData qui peuvent être consommées par des applications qui s'exécutent sur une variété de plateformes, incluant Windows Phone. Pour plus d'informations, lisez WCF Data Services.

Comme exemple, Netflix expose leur catalogue de films en tant que flux OData hébergé dans le Cloud. Nous consommons ce flux OData public dans l'exemple suivant:

Cliquez la tuile OData sur le téléphone pour démarrer l'exemple OData. Sur le téléphone, lorsque vous tapez le bouton Next Page, la page suivante est chargée depuis le service de données. Lorsque vous cliquez sur un élément de la liste, les informations détaillées de l'élément sélectionné sont affichées. Dans cet exemple, le bouton Back du téléphone, agit comme il le fait sur le téléphone en vous faisant revenir à la page précédente. Vous pouvez télécharger le projet complet dans la galerie de code Silverlight for Windows Phone.

Lier des données aux contrôles

Dans la bibliothèque cliente Odata pour Windows Phone 7, la classe DataServiceCollection représente une collection dynamique de données liées, qui fournit des notifications lorsque des éléments sont ajoutés ou supprimés de la collection. Une requête basée sur une URI détermine quelles données la collection va contenir. Cette URI est spécifiée en tant que paramètre dans la méthode LoadAsync de la classe DataServiceCollection. Lorsqu'elle est exécutée, cette méthode retourne un flux OData qui est matérialisé par des objets de données dans la collection liée. Les objets matérialisés sont gérés par la classe DataServiceCollection qui s'assure que les résultats sont envoyés sur le bon thread et ainsi vous n'avez pas besoin d'un objet Dispatcher. Pour plus d'informations sur l'utilisation de bibliothèque OData, lisez l'article Open Data Protocol (OData) Overview for Windows Phone.

Parce que les applications Windows Phone requièrent une navigation entre différentes pages, vous devriez utiliser un design pattern Model-View-ViewModel (MVVM) pour vos applications de données. Dans ce pattern, le modèle est généré par des outils basés sur les metadata retournées par le service de données, la vue est formée par tous les contrôlés liés de la page, et le ViewModel est un composant partagé qui s'occupe d'accéder au service de données et expose les données qui sont liées à la vue. En utilisant cette approche, vous pouvez exposer le DataServiceContext en tant que propriété de la classe ViewModel, à côté des propriétés qui retournent des instances de DataServiceCollection ou n'importe quelles autres valeurs dynamiques qui sont liées aux contrôles dans la vue. Le viewModel doit aussi exposer toutes les propriétés qui doivent être enregistrées lorsque l'application est désactivée, ce que nous discuterons dans une section plus loin. Le diagramme objet suivant représente la classe MainViewModel de notre exemple:

image_thumb2

C#

  1. // Defines the root URI of the data service.

  2. private static readonly Uri rootUri = new Uri("https://odata.netflix.com/v1/Catalog/");

  3. // Define the typed DataServiceContext.

  4. private NetflixCatalog _context;

  5. // Define the binding collection for Titles.

  6. private DataServiceCollection<Title> _titles;

  7. // Gets and sets the collection of Title objects from the feed.

  8. public DataServiceCollection<Title> Titles

  9. {

  10. get { return _titles; }

  11. private set

  12. {

  13. // Set the Customers collection.

  14. _titles = value;

  15. // Register a handler for the LoadCompleted callback.

  16. _titles.LoadCompleted += OnTitlesLoaded;

  17. // Raise the PropertyChanged events.

  18. NotifyPropertyChanged("Titles");

  19. }

  20. }

Visual Basic

  1. ' Defines the root URI of the data service.

  2. Private Shared ReadOnly rootUri As New Uri("https://odata.netflix.com/v1/Catalog/")

  3. ' Define the typed DataServiceContext.

  4. Private _context As NetflixCatalog

  5. ' Define the binding collection for Titles.

  6. Private _titles As DataServiceCollection(Of Title)

  7. ' Gets and sets the collection of Title objects from the feed.

  8. Public Property Titles() As DataServiceCollection(Of Title)

  9. Get

  10. Return _titles

  11. End Get

  12. Private Set(value As DataServiceCollection(Of Title))

  13. ' Set the Customers collection.

  14. _titles = value

  15. ' Register a handler for the LoadCompleted callback.

  16. _titles.LoadCompleted += OnTitlesLoaded

  17. ' Raise the PropertyChanged events.

  18. NotifyPropertyChanged("Titles")

  19. End Set

  20. End Property

La classe MainViewModel elle-même est exposée en tant que propriété statique de la classe d'application racine (app), comme montrée dans le code suivant:

C#

  1. static MainViewModel _viewModel = null;

  2. // A static ViewModel used by the views to bind against.

  3. public static

  4. MainViewModel

  5. ViewModel

  6. {

  7. get

  8. {

  9. // Delay creation of the view model until we need it.

  10. if (_viewModel == null)

  11. {

  12. _viewModel = new MainViewModel();

  13. }

  14. return _viewModel;

  15. }

  16. }

Visual Basic

  1. Shared _viewModel As MainViewModel = Nothing

  2. ' A static ViewModel used by the views to bind against.

  3. Public Shared ReadOnly Property ViewModel() As MainViewModel

  4. Get

  5. ' Delay creation of the view model until we need it.

  6. If _viewModel Is Nothing Then

  7. _viewModel = New MainViewModel()

  8. End If

  9. Return _viewModel

  10. End Get

  11. End Property

Le ViewModel est définit sur le DataContext de la page qui nous permet de lier le contrôle Listbox à la DataServiceCollection retournée par la propriété Titles de la classe MainViewModel. Vous pouvez voir dans l'exemple suivant que la Listbox et quelques autres éléments sont liés aux propriétés du MainViewModel:

XAML

  1. <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

  2. <ListBox Margin="0,0,-12,0" ItemsSource="{Binding Titles}"

  3. SelectionChanged="OnSelectionChanged" Height="Auto">

  4. <ListBox.ItemTemplate>

  5. <DataTemplate>

  6. <StackPanel Margin="0,0,0,17" Width="432" Orientation="Horizontal">

  7. <Image Source="{Binding Path=StreamUri}"

  8. Height="75" Width="50" />

  9. <StackPanel Orientation="Vertical">

  10. <StackPanel.Resources>

  11. <converter:TruncateSynopsis x:Key="synopsis" />

  12. </StackPanel.Resources>

  13. <TextBlock Text="{Binding Path=ShortName}" TextWrapping="Wrap"

  14. Style="{StaticResource PhoneTextLargeStyle}"/>

  15. <TextBlock Text="{Binding Path=ShortSynopsis,

  16. Converter={StaticResource synopsis}}"

  17. TextWrapping="Wrap" Margin="12,-6,12,0"

  18. Style="{StaticResource PhoneTextSubtleStyle}"/>

  19. </StackPanel>

  20. </StackPanel>

  21. </DataTemplate>

  22. </ListBox.ItemTemplate>

  23. </ListBox>

  24. </Grid>

  25. <StackPanel Grid.Row="2" Orientation="Horizontal">

  26. <TextBlock Name="PagingInfo" Text="{Binding PagesLoadedText}" Margin="12,20,10,28"

  27. Width="270" Style="{StaticResource PhoneTextNormalStyle}" />

  28. <Button Name="MoreButton" Content="Next Page"

  29. HorizontalAlignment="Right" Width="185" Height="80" Click="MoreButton_Click" />

  30. </StackPanel>

Lorsque la page MainPage.xaml se charge, la propriété IsDataLoaded property de la vue est vérifiée. Lorsque les données ne sont pas encore chargées, la méthode LoadData est appelée pour définir le binding, et demande la liste de titre du service de données.

C#

  1. // Used to determine whether the data is loaded.

  2. public bool IsDataLoaded { get; private set; }

  3. // Loads data when the application is initialized.

  4. public void LoadData()

  5. {

  6. // Instantiate the context and binding collection.

  7. Context = new NetflixCatalog(_rootUri);

  8. Titles = new DataServiceCollection<Title>(Context);

  9. // Load the data.

  10. Titles.LoadAsync(GetQueryUri());

  11. }

Visual Basic

  1. ' Used to determine whether the data is loaded.

  2. Public Property IsDataLoaded() As Boolean

  3. Get

  4. Return m_IsDataLoaded

  5. End Get

  6. Private Set(value As Boolean)

  7. m_IsDataLoaded = Value

  8. End Set

  9. End Property

  10. Private m_IsDataLoaded As Boolean

  11. ' Loads data when the application is initialized.

  12. Public Sub LoadData()

  13. ' Instantiate the context and binding collection.

  14. Context = New NetflixCatalog(_rootUri)

  15. Titles = New DataServiceCollection(Of Title)(Context)

  16. ' Load the data.

  17. Titles.LoadAsync(GetQueryUri())

  18. End Sub

Lorsque la méthode LoadAsync est appelée, la bibliothèque client envoie une requête asynchrone au service OData qui retourne un flux Atom des entrées Titles. Lorsque la réponse est reçue, l'évènement LoadCompleted est déclenché et les entrées dans le flux sont matérialisées par le client dans des objets dans la collection de Titles, qui est liée au contrôle ListBox.

OData définit un mécanisme pour accéder aux données binaires séparément de l'entité à laquelle elles appartiennent. De cette façon, un service de données peut exposer de nombreuses données binaires en tant que ressources média qui appartiennent à une entrée de lien média. Dans le catalogue Netflix, l'entité Title est une entrée de lien média avec une ressource média liée, qui est la couverture du titre. La méthode GetReadStreamUri du viewmodel appelle la méthode GetReadStreamUri sur le DataServiceContext pour retourner l'URI de la ressource média.

C#

  1. // Extend the Title class to bind to the media resource URI.
  2. public partial class Title
  3. {
  4. // Returns the media resource URI for binding.
  5. public Uri StreamUri
  6. {
  7. get
  8. {
  9. // Get the URI for the media resource stream.
  10. return App.ViewModel.GetReadStreamUri(this);
  11. }
  12. }
  13. }

Visual Basic

  1. ' Extend the Title class to bind to the media resource URI.

  2. Partial Public Class Title

  3. ' Returns the media resource URI for binding.

  4. Public ReadOnly Property StreamUri() As Uri

  5. Get

  6. ' Get the URI for the media resource stream.

  7. Return App.ViewModel.GetReadStreamUri(Me)

  8. End Get

  9. End Property

  10. End Class

Dans cet exemple, la propriété StreamUri appelle la méthode GetReadStreamUri pour retourner l'URI de ressource qui est une image de couverture. Lorsque nous lions la propriété StreamURI à l'attribut Source d'un contrôle Image, l'URI retournée est utilisée pour télécharger et afficher la couverture pour chaque titre.

Recommandation

Pour une entrée de lien média, nous recommandons que vous obteniez l'URI de la ressource en appelant la méthode GetReadStreamUri() plutôt qu'une autre propriété de l'entité. Bien que l'URI de la ressource média marche bien dans ce cas pour créer une image sur le client, d'autres méthodes du DataServiceContext peuvent être utilisées pour accéder et changer les ressources média en tant que stream binaire.

Paging et Navigation

Dans cet exemple, l'URI de requête est construite en utilisant les paramètres de pagination définis dans l'application. Dans OData, il existe deux sortes de paging. Un client peut utiliser les options de requête $top et $skip pour limiter le nombre d'entrées dans la réponse dans une page logique plutôt que l'ensemble des données. Le service de données lui-même peut aussi limiter le nombre d'entrées retournées par une réponse donnée, lorsque la réponse contient un token (jeton) de continuation qui est utilisé pour obtenir la page suivante de données à partir du service de données.

Parce que le client n'est pas capable de déterminer si un service de données implémente un paging piloté par le serveur ou simplement la taille d'une page, vous devriez considérer à implémenter un paging côté client dans vos applications Windows Phone pour s'assurer qu'aucune requête ne retourne un trop gros ensemble de données, ce qui prendrait du temps et consommerait les ressources du téléphone. Vous devriez aussi être préparé à prendre en compte du paging côté serveur dans votre application.

L'exemple suivant montre la méthode GetQueryUri() qui retourne l'URI de requête, qui est créée en se basant sur les variables passées aux paramètres $top et $skip.

C#

  1. // Private method that returns the page-specific query.

  2. private Uri GetQueryUri()

  3. {

  4. // Construct and return the query URI for the next page of results.

  5. var queryUri = new Uri(string.Format("/Titles?$top={0}&$skip={1}",

  6. _pageSize, _currentPage * _pageSize), UriKind.Relative);

  7. if (_currentPage == 0)

  8. {

  9. // If this is the first page, then also include a count of all titles.

  10. queryUri = new Uri(queryUri.ToString() +

  11. "&$inlinecount=allpages", UriKind.Relative);

  12. }

  13. return queryUri;

  14. }

Visual Basic

  1. ' Private method that returns the page-specific query.

  2. Private Function GetQueryUri() As Uri

  3. ' Construct and return the query URI for the next page of results.

  4. Dim queryUri = New Uri(String.Format("/Titles?$top={0}&$skip={1}", _pageSize, _currentPage * _pageSize), UriKind.Relative)

  5. If _currentPage = 0 Then

  6. ' If this is the first page, then also include a count of all titles.

  7. queryUri = New Uri(queryUri.ToString() & "&$inlinecount=allpages", UriKind.Relative)

  8. End If

  9. Return queryUri

  10. End Function

Dans cet exemple, la taille de page est fixe et le nombre d'entrée à sauter est calculé en fonction du nombre de la page courante. Lorsque nous chargeons la première page, nous incluons l'option de requête $inlinecount=allpages pour demander le compte total de toutes les entités Titles, qui est utilisée pour calculer le nombre de pages qui est affiché dans TitlesPage.xaml.

Lorsque la réponse est reçue, le client déclenche l'évènement LoadCompleted. Lorsqu'il est nécessaire de charger des pages supplémentaires à cause du paging côté serveur, la propriété Continuation de l'objet DataServiceCollection retourne un token(jeton) de continuation. La méthode suivante gère l'évènement LoadCompleted pour charger les pages:

C#

  1. private void OnTitlesLoaded(object sender, LoadCompletedEventArgs e)

  2. {

  3. // Make sure that we load all pages of the Customers feed.

  4. if (Titles.Continuation != null)

  5. {

  6. Titles.LoadNextPartialSetAsync();

  7. }

  8. // Set the total page count, if we requested one.

  9. if (e.QueryOperationResponse.Query

  10. .RequestUri.Query.Contains("$inlinecount=allpages"))

  11. {

  12. _totalCount = (int)e.QueryOperationResponse.TotalCount;

  13. }

  14. IsDataLoaded = true;

  15. // Update the pages loaded text binding.

  16. NotifyPropertyChanged("PagesLoadedText");

  17. }

Visual Basic

  1. Private Sub OnTitlesLoaded(sender As Object, e As LoadCompletedEventArgs)

  2. ' Make sure that we load all pages of the Customers feed.

  3. If Titles.Continuation IsNot Nothing Then

  4. Titles.LoadNextPartialSetAsync()

  5. End If

  6. ' Set the total page count, if we requested one.

  7. If e.QueryOperationResponse.Query.RequestUri.Query.Contains("$inlinecount=allpages") Then

  8. _totalCount = CInt(e.QueryOperationResponse.TotalCount)

  9. End If

  10. IsDataLoaded = True

  11. ' Update the pages loaded text binding.

  12. NotifyPropertyChanged("PagesLoadedText")

  13. End Sub

Notez que nous lisons aussi et enregistrons la valeur TotalCount lorsque celle-ci est demandée dans la requête. Cette valeur est utilisée pour calculer et afficher le nombre total de pages. Parce qu'une version de TotalCount mise à jour change la valeur de la propriété PagesLoadedText, nous reportons aussi ce changement jusqu'au binding.

La page de données suivante est demandée lorsque l'utilisateur appuie sur le bouton Next Page. Vous pourriez créer un nouvel objet DataServiceCollection basé sur l'URI qui retourne la page suivant, l'exemple suivant utilise au contraire la navigation pour charger une page spécifique de données:

Code Snippet

  1. private void MoreButton_Click(object sender, RoutedEventArgs e)
  2. {
  3. if (App.ViewModel.IsDataLoaded)
  4. {
  5. // Navigate to the next page of data.
  6. this.NavigationService.Navigate(
  7. new Uri("/TitlesPage.xaml?page="
  8. + (App.ViewModel.CurrentPage + 1), UriKind.Relative));
  9. }
  10. }

Visual Basic

  1. Private Sub MoreButton_Click(sender As Object, e As RoutedEventArgs)
  2. If App.ViewModel.IsDataLoaded Then
  3. ' Navigate to the next page of data.
  4. Me.NavigationService.Navigate(New Uri("/TitlesPage.xaml?page=" & (App.ViewModel.CurrentPage + 1), UriKind.Relative))
  5. End If
  6. End Sub

Cette navigation recharge principalement la page MainPage.xaml avec la page suivante.

C#

  1. protected override void OnNavigatedTo(NavigationEventArgs e)

  2. {

  3. if (!App.ViewModel.IsDataLoaded)

  4. {

  5. App.ViewModel.LoadData();

  6. }

  7. else

  8. {

  9. if (this.NavigationContext.QueryString.Count == 1)

  10. {

  11. // Get the value of the requested page.

  12. int page = int.Parse(this.NavigationContext.QueryString["page"]);

  13. // Check to see if the page is currently loaded.

  14. if (page != App.ViewModel.CurrentPage)

  15. {

  16. // Load data for the specific page.

  17. App.ViewModel.LoadData(page);

  18. }

  19. }

  20. else

  21. {

  22. // If there is no query parameter, we are at the first page.

  23. App.ViewModel.LoadData(0);

  24. }

  25. }

  26. }

Visual Basic

  1. Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)

  2. If Not App.ViewModel.IsDataLoaded Then

  3. App.ViewModel.LoadData()

  4. Else

  5. If Me.NavigationContext.QueryString.Count = 1 Then

  6. ' Get the value of the requested page.

  7. Dim page As Integer = Integer.Parse(Me.NavigationContext.QueryString("page"))

  8. ' Check to see if the page is currently loaded.

  9. If page <> App.ViewModel.CurrentPage Then

  10. ' Load data for the specific page.

  11. App.ViewModel.LoadData(page)

  12. End If

  13. Else

  14. ' If there is no query parameter, we are at the first page.

  15. App.ViewModel.LoadData(0)

  16. End If

  17. End If

  18. End Sub

En utilisant la navigation pour charger les pages suivantes, lorsque l'utilisateur clique sur le bouton Retour, le comportement par défaut est de recharger la page précédente. Sans la navigation, vous auriez besoin de surcharger la méthode OnBackKeyPress pour charger la page précédente.

Lorsque l'utilisateur clique sur un titre dans le contrôle ListBox, la méthode suivante navigue vers la page TitlesDetailPage.xaml pour le titre sélectionné :

C#

  1. private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)

  2. {

  3. var selector = (Selector)sender;

  4. if (selector.SelectedIndex == -1)

  5. {

  6. return;

  7. }

  8. this.NavigationService.Navigate(

  9. new Uri("/TitleDetailsPage.xaml?selectedIndex="

  10. + selector.SelectedIndex, UriKind.Relative));

  11. selector.SelectedIndex = -1;

  12. }

Visual Basic

  1. Private Sub OnSelectionChanged(sender As Object, e As SelectionChangedEventArgs)

  2. Dim selector = DirectCast(sender, Selector)

  3. If selector.SelectedIndex = -1 Then

  4. Return

  5. End If

  6. Me.NavigationService.Navigate(New Uri("/TitleDetailsPage.xaml?selectedIndex=" & Convert.ToString(selector.SelectedIndex), UriKind.Relative))

  7. selector.SelectedIndex = -1

  8. End Sub

Dans la page TitleDetailsPage.xaml, la valeur de l'index sélectionné est utilisée pour lier le bon Title depuis la collection de Titles, comme ceci :

C#

  1. protected override void OnNavigatedTo(NavigationEventArgs e)
  2. {
  3. string indexAsString = this.NavigationContext.QueryString["selectedIndex"];
  4. int index = int.Parse(indexAsString);
  5. this.DataContext = this.currentTitle
  6. = (Title)App.ViewModel.Titles[index];
  7. }

Visual Basic

  1. Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
  2. Dim indexAsString As String = Me.NavigationContext.QueryString("selectedIndex")
  3. Dim index As Integer = Integer.Parse(indexAsString)
  4. Me.DataContext = InlineAssignHelper(Me.currentTitle, DirectCast(App.ViewModel.Titles(index), Title))
  5. End Sub

Maintenir l'état d'application

Le système d'exploitation Windows Phone permet seulement à une seule application de s'exécuter à un moment donné. Lorsque l'utilisateur quitte une application, le système éteint cette application. Pour permettre à l'application d'être réactivée correctement, les données chargées, les informations d'état des objets et certaines propriétés du ViewModel doivent être stockées dans le dictionnaire d'état. Le client OData pour Windows Phone 7 introduit une classe DataServiceState qui est utilisée pour stocker le DataServiceContext et une collection d'objets DataServiceCollection. La méthode suivante SaveState dans le ViewModel retourne cet objet DataServiceState dans une collection clé-valeur d'éléments, ainsi que d'autres données du ViewModel qui doivent être persistées:

C#

  1. // Return a collection of key-value pairs to store in the application state.

  2. public List<KeyValuePair<string, object>> SaveState()

  3. {

  4. if (App.ViewModel.IsDataLoaded)

  5. {

  6. List<KeyValuePair<string, object>> stateList

  7. = new List<KeyValuePair<string, object>>();

  8. // Create a new dictionary to store binding collections.

  9. var collections = new Dictionary<string, object>();

  10. // Add the current Titles binding collection.

  11. collections["Titles"] = App.ViewModel.Titles;

  12. // Store the current context and binding collections in the view model state.

  13. stateList.Add(new KeyValuePair<string, object>(

  14. "DataServiceState", DataServiceState.Save(_context, collections)));

  15. stateList.Add(new KeyValuePair<string, object>("CurrentPage", CurrentPage));

  16. stateList.Add(new KeyValuePair<string, object>("TotalCount", TotalCount));

  17. return stateList;

  18. }

  19. else

  20. {

  21. return null;

  22. }

  23. }

Visual Basic

  1. ' Return a collection of key-value pairs to store in the application state.

  2. Public Function SaveState() As List(Of KeyValuePair(Of String, Object))

  3. If App.ViewModel.IsDataLoaded Then

  4. Dim stateList As New List(Of KeyValuePair(Of String, Object))()

  5. ' Create a new dictionary to store binding collections.

  6. Dim collections = New Dictionary(Of String, Object)()

  7. ' Add the current Titles binding collection.

  8. collections("Titles") = App.ViewModel.Titles

  9. ' Store the current context and binding collections in the view model state.

  10. stateList.Add(New KeyValuePair(Of String, Object)("DataServiceState", DataServiceState.Save(_context, collections)))

  11. stateList.Add(New KeyValuePair(Of String, Object)("CurrentPage", CurrentPage))

  12. stateList.Add(New KeyValuePair(Of String, Object)("TotalCount", TotalCount))

  13. Return stateList

  14. Else

  15. Return Nothing

  16. End If

  17. End Function

La méthode suivante gère l'évènement Deactivated et appelle la méthode SaveState pour stocker les données du viewmodel lorsque l'application est désactivée:

C#

  1. // Code to execute when the application is deactivated (sent to background).
  2. // This code will not execute when the application is closing.
  3. private void Application_Deactivated(object sender, DeactivatedEventArgs e)
  4. {
  5. if (App.ViewModel.IsDataLoaded)
  6. {
  7. // Store each key-value pair in the state dictionary.
  8. foreach (KeyValuePair<string, object> item in App.ViewModel.SaveState())
  9. {
  10. PhoneApplicationService.Current.State[item.Key] = item.Value;
  11. }
  12. }
  13. }

Visual Basic

  1. ' Code to execute when the application is deactivated (sent to background).
  2. ' This code will not execute when the application is closing.
  3. Private Sub Application_Deactivated(sender As Object, e As DeactivatedEventArgs)
  4. If App.ViewModel.IsDataLoaded Then
  5. ' Store each key-value pair in the state dictionary.
  6. For Each item As KeyValuePair(Of String, Object) In App.ViewModel.SaveState()
  7. PhoneApplicationService.Current.State(item.Key) = item.Value
  8. Next
  9. End If
  10. End Sub

Lorsque l'application est réactivée, le ViewModel est récupéré depuis le dictionnaire d'état. La méthode suivante gère l'évènement Activated et restaure l'application dans son ancien état:

C#

  1. // Code to execute when the application is activated (brought to foreground).
  2. // This code will not execute when the application is first launched.
  3. private void Application_Activated(object sender, ActivatedEventArgs e)
  4. {
  5. App.ViewModel.RestoreState(PhoneApplicationService.Current.State);
  6. }

Visual Basic

  1. ' Code to execute when the application is activated (brought to foreground).
  2. ' This code will not execute when the application is first launched.
  3. Private Sub Application_Activated(sender As Object, e As ActivatedEventArgs)
  4. App.ViewModel.RestoreState(PhoneApplicationService.Current.State)
  5. End Sub

Dans la méthode RestoreState suivante, l'objet DataServiceState et les autres données enregistrées sont acquises depuis le dictionnaire d'état global et utilisées pour réinitialiser le viewmodel:

C#

  1. // Restores the view model state from the supplied state dictionary.

  2. public void RestoreState(IDictionary<string, object> dictionary)

  3. {

  4. // Create a dictionary to hold any stored binding collections.

  5. Dictionary<string, object> collections;

  6. // Get the stored DataServiceState object from the dictionary.

  7. var state = dictionary["DataServiceState"] as DataServiceState;

  8. if (state != null)

  9. {

  10. // Restore the context and binding collections.

  11. NetflixCatalog context

  12. = state.Restore(out collections) as NetflixCatalog;

  13. // Get the binding collection of Title objects.

  14. DataServiceCollection<Title> titles

  15. = collections["Titles"] as DataServiceCollection<Title>;

  16. // Initialize the application with stored data.

  17. App.ViewModel.LoadData(context, titles);

  18. // Restore other view model data.

  19. _currentPage = (int)dictionary["CurrentPage"];

  20. _totalCount = (int)dictionary["TotalCount"];

  21. }

  22. }

Visual Basic

  1. ' Restores the view model state from the supplied state dictionary.

  2. Public Sub RestoreState(dictionary As IDictionary(Of String, Object))

  3. ' Create a dictionary to hold any stored binding collections.

  4. Dim collections As Dictionary(Of String, Object)

  5. ' Get the stored DataServiceState object from the dictionary.

  6. Dim state = TryCast(dictionary("DataServiceState"), DataServiceState)

  7. If state IsNot Nothing Then

  8. ' Restore the context and binding collections.

  9. Dim context As NetflixCatalog = TryCast(state.Restore(collections), NetflixCatalog)

  10. ' Get the binding collection of Title objects.

  11. Dim titles As DataServiceCollection(Of Title) = TryCast(collections("Titles"), DataServiceCollection(Of Title))

  12. ' Initialize the application with stored data.

  13. App.ViewModel.LoadData(context, titles)

  14. ' Restore other view model data.

  15. _currentPage = CInt(dictionary("CurrentPage"))

  16. _totalCount = CInt(dictionary("TotalCount"))

  17. End If

  18. End Sub

Générer les classes de données du client

Vous pouvez utiliser l'outil DataSvcUtil.exe pour générer les classes de données dans votre application qui représente le modèle de données d'un service OData. Cet outil, qui est inclut avec les bibliothèques OData sur CodePlex, se connecte au service de données et génère les classes de données et le containeur de données, qui hérite de la classe DataServiceContext. La commande suivante génère le modèle de données client basé sur le service OData Netflix:

Command

DataSvcUtil.exe /out:"Netflix.cs" /uri:"https://odata.netflix.com/v1/Catalog" /language:csharp /DataServiceCollection /version:2.0

En utilisant le paramètre /DataServiceCollection dans la commande, les classes DataServiceCollection sont générées pour chaque collection dans le modèle.

L'outil DataSvcUtil.exe génère le containeur de données avec le même nom que l'espace de nom du schéma. Pour simplifier les références, l'espace de nom des classes de données générées ont été modifiées manuellement.

Voir aussi


Cliquez ici pour revenir au sommaire de la liste d’articles