Appel de l’API web à partir d’une application Windows Phone 8 (C#)

par Robert McMurray

Dans ce tutoriel, vous allez apprendre à créer un scénario complet de bout en bout composé d’une application API Web ASP.NET qui fournit un catalogue de livres à une application Windows Phone 8.

Vue d’ensemble

Les services RESTful tels que API Web ASP.NET simplifient la création d’applications HTTP pour les développeurs en abstraitant l’architecture des applications côté serveur et côté client. Au lieu de créer un protocole propriétaire basé sur un socket pour la communication, les développeurs d’API web doivent simplement publier les méthodes HTTP requises pour leur application (par exemple: GET, POST, PUT, DELETE), et les développeurs d’applications clientes doivent uniquement utiliser les méthodes HTTP nécessaires pour leur application.

Dans ce tutoriel de bout en bout, vous allez apprendre à utiliser l’API web pour créer les projets suivants :

Prérequis

  • Visual Studio 2013 avec le Kit de développement logiciel (SDK) Windows Phone 8 installé
  • Windows 8 ou version ultérieure sur un système 64 bits avec Hyper-V installé
  • Pour obtenir la liste des exigences supplémentaires, consultez la section Configuration requise sur la page de téléchargement du KIT de développement logiciel (SDK) Windows Phone 8.0.

Notes

Si vous souhaitez tester la connectivité entre l’API web et Windows Phone 8 projets sur votre système local, vous devez suivre les instructions de l’article Connexion de l’émulateur Windows Phone 8 aux applications d’API web sur un ordinateur local pour configurer votre environnement de test.

Étape 1 : Création du projet Web API Bookstore

La première étape de ce tutoriel de bout en bout consiste à créer un projet d’API web qui prend en charge toutes les opérations CRUD ; Notez que vous allez ajouter le projet d’application Windows Phone à cette solution à l’étape 2 de ce didacticiel.

  1. Ouvrez Visual Studio 2013.

  2. Cliquez sur Fichier, puis sur Nouveau, puis sur Projet.

  3. Lorsque la boîte de dialogue Nouveau projet s’affiche, développez Installé, puis Modèles, puis Visual C#, puis Web.

    Capture d’écran de la boîte de dialogue « Nouveau projet », montrant le chemin du fichier dans le menu et mettant en évidence les étapes de création du nouveau projet.
    Cliquez sur l’image pour développer
  4. Mettez en surbrillance ASP.NET’application web, entrez BookStore pour le nom du projet, puis cliquez sur OK.

  5. Lorsque la boîte de dialogue Nouveau projet ASP.NET s’affiche, sélectionnez le modèle d’API web , puis cliquez sur OK.

    Capture d’écran de la boîte de dialogue Librairie de projet A S P dot NET, montrant les options de modèle et les zones case activée pour sélectionner le dossier de modèle et la référence principale.
    Cliquez sur l’image pour développer
  6. Lorsque le projet d’API web s’ouvre, supprimez l’exemple de contrôleur du projet :

    1. Développez le dossier Contrôleurs dans l’Explorateur de solutions.
    2. Cliquez avec le bouton droit sur le fichier ValuesController.cs , puis cliquez sur Supprimer.
    3. Cliquez sur OK lorsque vous êtes invité à confirmer la suppression.
  7. Ajouter un fichier de données XML au projet d’API web ; ce fichier contient le contenu du catalogue bookstore :

    1. Cliquez avec le bouton droit sur le dossier App_Data dans l’Explorateur de solutions, cliquez sur Ajouter, puis sur Nouvel élément.

    2. Lorsque la boîte de dialogue Ajouter un nouvel élément s’affiche, mettez en surbrillance le modèle Fichier XML .

    3. Nommez le fichier Books.xml, puis cliquez sur Ajouter.

    4. Lorsque le fichier Books.xml est ouvert, remplacez le code dans le fichier par le code XML de l’exemple de fichierbooks.xml sur MSDN :

      <?xml version="1.0" encoding="utf-8"?>
      <catalog>
        <book id="bk101">
          <author>Gambardella, Matthew</author>
          <title>XML Developer's Guide</title>
          <genre>Computer</genre>
          <price>44.95</price>
          <publish_date>2000-10-01</publish_date>
          <description>
            An in-depth look at creating applications
            with XML.
          </description>
        </book>
        <book id="bk102">
          <author>Ralls, Kim</author>
          <title>Midnight Rain</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2000-12-16</publish_date>
          <description>
            A former architect battles corporate zombies,
            an evil sorceress, and her own childhood to become queen
            of the world.
          </description>
        </book>
        <book id="bk103">
          <author>Corets, Eva</author>
          <title>Maeve Ascendant</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2000-11-17</publish_date>
          <description>
            After the collapse of a nanotechnology
            society in England, the young survivors lay the
            foundation for a new society.
          </description>
        </book>
        <book id="bk104">
          <author>Corets, Eva</author>
          <title>Oberon's Legacy</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2001-03-10</publish_date>
          <description>
            In post-apocalypse England, the mysterious
            agent known only as Oberon helps to create a new life
            for the inhabitants of London. Sequel to Maeve
            Ascendant.
          </description>
        </book>
        <book id="bk105">
          <author>Corets, Eva</author>
          <title>The Sundered Grail</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2001-09-10</publish_date>
          <description>
            The two daughters of Maeve, half-sisters,
            battle one another for control of England. Sequel to
            Oberon's Legacy.
          </description>
        </book>
        <book id="bk106">
          <author>Randall, Cynthia</author>
          <title>Lover Birds</title>
          <genre>Romance</genre>
          <price>4.95</price>
          <publish_date>2000-09-02</publish_date>
          <description>
            When Carla meets Paul at an ornithology
            conference, tempers fly as feathers get ruffled.
          </description>
        </book>
        <book id="bk107">
          <author>Thurman, Paula</author>
          <title>Splish Splash</title>
          <genre>Romance</genre>
          <price>4.95</price>
          <publish_date>2000-11-02</publish_date>
          <description>
            A deep sea diver finds true love twenty
            thousand leagues beneath the sea.
          </description>
        </book>
        <book id="bk108">
          <author>Knorr, Stefan</author>
          <title>Creepy Crawlies</title>
          <genre>Horror</genre>
          <price>4.95</price>
          <publish_date>2000-12-06</publish_date>
          <description>
            An anthology of horror stories about roaches,
            centipedes, scorpions  and other insects.
          </description>
        </book>
        <book id="bk109">
          <author>Kress, Peter</author>
          <title>Paradox Lost</title>
          <genre>Science Fiction</genre>
          <price>6.95</price>
          <publish_date>2000-11-02</publish_date>
          <description>
            After an inadvertant trip through a Heisenberg
            Uncertainty Device, James Salway discovers the problems
            of being quantum.
          </description>
        </book>
        <book id="bk110">
          <author>O'Brien, Tim</author>
          <title>Microsoft .NET: The Programming Bible</title>
          <genre>Computer</genre>
          <price>36.95</price>
          <publish_date>2000-12-09</publish_date>
          <description>
            Microsoft's .NET initiative is explored in
            detail in this deep programmer's reference.
          </description>
        </book>
        <book id="bk111">
          <author>O'Brien, Tim</author>
          <title>MSXML3: A Comprehensive Guide</title>
          <genre>Computer</genre>
          <price>36.95</price>
          <publish_date>2000-12-01</publish_date>
          <description>
            The Microsoft MSXML3 parser is covered in
            detail, with attention to XML DOM interfaces, XSLT processing,
            SAX and more.
          </description>
        </book>
        <book id="bk112">
          <author>Galos, Mike</author>
          <title>Visual Studio 7: A Comprehensive Guide</title>
          <genre>Computer</genre>
          <price>49.95</price>
          <publish_date>2001-04-16</publish_date>
          <description>
            Microsoft Visual Studio 7 is explored in depth,
            looking at how Visual Basic, Visual C++, C#, and ASP+ are
            integrated into a comprehensive development
            environment.
          </description>
        </book>
      </catalog>
      
    5. Enregistrez et fermez le fichier XML.

  8. Ajoutez le modèle bookstore au projet d’API web ; ce modèle contient la logique CRUD (Create, Read, Update et Delete) pour l’application bookstore :

    1. Cliquez avec le bouton droit sur le dossier Modèles dans l’Explorateur de solutions, cliquez sur Ajouter, puis sur Classe.

    2. Lorsque la boîte de dialogue Ajouter un nouvel élément s’affiche, nommez le fichier de classe BookDetails.cs, puis cliquez sur Ajouter.

    3. Lorsque le fichier BookDetails.cs est ouvert, remplacez le code dans le fichier par les éléments suivants :

      using System;
      using System.Collections.Generic;
      using System.ComponentModel.DataAnnotations;
      using System.Linq;
      using System.Xml;
      using System.Xml.Linq;
      using System.Xml.XPath;
      using System.Web;
      
      namespace BookStore.Models
      {
          /// <summary>
          /// Define a class that will hold the detailed information for a book.
          /// </summary>
          public class BookDetails
          {
              [Required]
              public String Id { get; set; }
              [Required]
              public String Title { get; set; }
              public String Author { get; set; }
              public String Genre { get; set; }
              public Decimal Price { get; set; }
              public DateTime PublishDate { get; set; }
              public String Description { get; set; }
          }
      
          /// <summary>
          /// Define an interface which contains the methods for the book repository.
          /// </summary>
          public interface IBookRepository
          {
              BookDetails CreateBook(BookDetails book);
              IEnumerable<BookDetails> ReadAllBooks();
              BookDetails ReadBook(String id);
              BookDetails UpdateBook(String id, BookDetails book);
              Boolean DeleteBook(String id);
          }
      
          /// <summary>
          /// Define a class based on the book repository interface which contains the method implementations.
          /// </summary>
          public class BookRepository : IBookRepository
          {
              private string xmlFilename = null;
              private XDocument xmlDocument = null;
      
              /// <summary>
              /// Define the class constructor.
              /// </summary>
              public BookRepository()
              {
                  try
                  {
                      // Determine the path to the books.xml file.
                      xmlFilename = HttpContext.Current.Server.MapPath("~/app_data/books.xml");
                      // Load the contents of the books.xml file into an XDocument object.
                      xmlDocument = XDocument.Load(xmlFilename);
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
              }
      
              /// <summary>
              /// Method to add a new book to the catalog.
              /// Defines the implementation of the POST method.
              /// </summary>
              public BookDetails CreateBook(BookDetails book)
              {
                  try
                  {
                      // Retrieve the book with the highest ID from the catalog.
                      var highestBook = (
                          from bookNode in xmlDocument.Elements("catalog").Elements("book")
                          orderby bookNode.Attribute("id").Value descending
                          select bookNode).Take(1);
                      // Extract the ID from the book data.
                      string highestId = highestBook.Attributes("id").First().Value;
                      // Create an ID for the new book.
                      string newId = "bk" + (Convert.ToInt32(highestId.Substring(2)) + 1).ToString();
                      // Verify that this book ID does not currently exist.
                      if (this.ReadBook(newId) == null)
                      {
                          // Retrieve the parent element for the book catalog.
                          XElement bookCatalogRoot = xmlDocument.Elements("catalog").Single();
                          // Create a new book element.
                          XElement newBook = new XElement("book", new XAttribute("id", newId));
                          // Create elements for each of the book's data items.
                          XElement[] bookInfo = FormatBookData(book);
                          // Add the element to the book element.
                          newBook.ReplaceNodes(bookInfo);
                          // Append the new book to the XML document.
                          bookCatalogRoot.Add(newBook);
                          // Save the XML document.
                          xmlDocument.Save(xmlFilename);
                          // Return an object for the newly-added book.
                          return this.ReadBook(newId);
                      }
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
                  // Return null to signify failure.
                  return null;
              }
      
              /// <summary>
              /// Method to retrieve all of the books in the catalog.
              /// Defines the implementation of the non-specific GET method.
              /// </summary>
              public IEnumerable<BookDetails> ReadAllBooks()
              {
                  try
                  {
                      // Return a list that contains the catalog of book ids/titles.
                      return (
                          // Query the catalog of books.
                          from book in xmlDocument.Elements("catalog").Elements("book")
                          // Sort the catalog based on book IDs.
                          orderby book.Attribute("id").Value ascending
                          // Create a new instance of the detailed book information class.
                          select new BookDetails
                          {
                              // Populate the class with data from each of the book's elements.
                              Id = book.Attribute("id").Value,
                              Author = book.Element("author").Value,
                              Title = book.Element("title").Value,
                              Genre = book.Element("genre").Value,
                              Price = Convert.ToDecimal(book.Element("price").Value),
                              PublishDate = Convert.ToDateTime(book.Element("publish_date").Value),
                              Description = book.Element("description").Value
                          }).ToList();
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
              }
      
              /// <summary>
              /// Method to retrieve a specific book from the catalog.
              /// Defines the implementation of the ID-specific GET method.
              /// </summary>
              public BookDetails ReadBook(String id)
              {
                  try
                  {
                      // Retrieve a specific book from the catalog.
                      return (
                          // Query the catalog of books.
                          from book in xmlDocument.Elements("catalog").Elements("book")
                          // Specify the specific book ID to query.
                          where book.Attribute("id").Value.Equals(id)
                          // Create a new instance of the detailed book information class.
                          select new BookDetails
                          {
                              // Populate the class with data from each of the book's elements.
                              Id = book.Attribute("id").Value,
                              Author = book.Element("author").Value,
                              Title = book.Element("title").Value,
                              Genre = book.Element("genre").Value,
                              Price = Convert.ToDecimal(book.Element("price").Value),
                              PublishDate = Convert.ToDateTime(book.Element("publish_date").Value),
                              Description = book.Element("description").Value
                          }).Single();
                  }
                  catch
                  {
                      // Return null to signify failure.
                      return null;
                  }
              }
      
              /// <summary>
              /// Populates a book BookDetails class with the data for a book.
              /// </summary>
              private XElement[] FormatBookData(BookDetails book)
              {
                  XElement[] bookInfo =
                  {
                      new XElement("author", book.Author),
                      new XElement("title", book.Title),
                      new XElement("genre", book.Genre),
                      new XElement("price", book.Price.ToString()),
                      new XElement("publish_date", book.PublishDate.ToString()),
                      new XElement("description", book.Description)
                  };
                  return bookInfo;
              }
      
              /// <summary>
              /// Method to update an existing book in the catalog.
              /// Defines the implementation of the PUT method.
              /// </summary>
              public BookDetails UpdateBook(String id, BookDetails book)
              {
                  try
                  {
                      // Retrieve a specific book from the catalog.
                      XElement updateBook = xmlDocument.XPathSelectElement(String.Format("catalog/book[@id='{0}']", id));
                      // Verify that the book exists.
                      if (updateBook != null)
                      {
                          // Create elements for each of the book's data items.
                          XElement[] bookInfo = FormatBookData(book);
                          // Add the element to the book element.
                          updateBook.ReplaceNodes(bookInfo);
                          // Save the XML document.
                          xmlDocument.Save(xmlFilename);
                          // Return an object for the updated book.
                          return this.ReadBook(id);
                      }
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
                  // Return null to signify failure.
                  return null;
              }
      
              /// <summary>
              /// Method to remove an existing book from the catalog.
              /// Defines the implementation of the DELETE method.
              /// </summary>
              public Boolean DeleteBook(String id)
              {
                  try
                  {
                      if (this.ReadBook(id) != null)
                      {
                          // Remove the specific child node from the catalog.
                          xmlDocument
                              .Elements("catalog")
                              .Elements("book")
                              .Where(x => x.Attribute("id").Value.Equals(id))
                              .Remove();
                          // Save the XML document.
                          xmlDocument.Save(xmlFilename);
                          // Return a success status.
                          return true;
                      }
                      else
                      {
                          // Return a failure status.
                          return false;
                      }
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
              }
          }
      }
      
    4. Enregistrez et fermez le fichier BookDetails.cs .

  9. Ajoutez le contrôleur bookstore au projet d’API web :

    1. Cliquez avec le bouton droit sur le dossier Contrôleurs dans l’Explorateur de solutions, cliquez sur Ajouter, puis sur Contrôleur.

    2. Lorsque la boîte de dialogue Ajouter une structure s’affiche, mettez en surbrillance Contrôleur d’API web 2 - Vide, puis cliquez sur Ajouter.

    3. Lorsque la boîte de dialogue Ajouter un contrôleur s’affiche, nommez le contrôleur BooksController, puis cliquez sur Ajouter.

    4. Lorsque le fichier BooksController.cs est ouvert, remplacez le code dans le fichier par les éléments suivants :

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Net;
      using System.Net.Http;
      using System.Web.Http;
      using BookStore.Models;
      
      namespace BookStore.Controllers
      {
          public class BooksController : ApiController
          {
              private BookRepository repository = null;
      
              // Define the class constructor.
              public BooksController()
              {
                  this.repository = new BookRepository();
              }
      
              /// <summary>
              /// Method to retrieve all of the books in the catalog.
              /// Example: GET api/books
              /// </summary>
              [HttpGet]
              public HttpResponseMessage Get()
              {
                  IEnumerable<BookDetails> books = this.repository.ReadAllBooks();
                  if (books != null)
                  {
                      return Request.CreateResponse<IEnumerable<BookDetails>>(HttpStatusCode.OK, books);
                  }
                  else
                  {
                      return Request.CreateResponse(HttpStatusCode.NotFound);
                  }
              }
      
              /// <summary>
              /// Method to retrieve a specific book from the catalog.
              /// Example: GET api/books/5
              /// </summary>
              [HttpGet]
              public HttpResponseMessage Get(String id)
              {
                  BookDetails book = this.repository.ReadBook(id);
                  if (book != null)
                  {
                      return Request.CreateResponse<BookDetails>(HttpStatusCode.OK, book);
                  }
                  else
                  {
                      return Request.CreateResponse(HttpStatusCode.NotFound);
                  }
              }
      
              /// <summary>
              /// Method to add a new book to the catalog.
              /// Example: POST api/books
              /// </summary>
              [HttpPost]
              public HttpResponseMessage Post(BookDetails book)
              {
                  if ((this.ModelState.IsValid) && (book != null))
                  {
                      BookDetails newBook = this.repository.CreateBook(book);
                      if (newBook != null)
                      {
                          var httpResponse = Request.CreateResponse<BookDetails>(HttpStatusCode.Created, newBook);
                          string uri = Url.Link("DefaultApi", new { id = newBook.Id });
                          httpResponse.Headers.Location = new Uri(uri);
                          return httpResponse;
                      }
                  }
                  return Request.CreateResponse(HttpStatusCode.BadRequest);
              }
      
              /// <summary>
              /// Method to update an existing book in the catalog.
              /// Example: PUT api/books/5
              /// </summary>
              [HttpPut]
              public HttpResponseMessage Put(String id, BookDetails book)
              {
                  if ((this.ModelState.IsValid) && (book != null) && (book.Id.Equals(id)))
                  {
                      BookDetails modifiedBook = this.repository.UpdateBook(id, book);
                      if (modifiedBook != null)
                      {
                          return Request.CreateResponse<BookDetails>(HttpStatusCode.OK, modifiedBook);
                      }
                      else
                      {
                          return Request.CreateResponse(HttpStatusCode.NotFound);
                      }
                  }
                  return Request.CreateResponse(HttpStatusCode.BadRequest);
              }
      
              /// <summary>
              /// Method to remove an existing book from the catalog.
              /// Example: DELETE api/books/5
              /// </summary>
              [HttpDelete]
              public HttpResponseMessage Delete(String id)
              {
                  BookDetails book = this.repository.ReadBook(id);
                  if (book != null)
                  {
                      if (this.repository.DeleteBook(id))
                      {
                          return Request.CreateResponse(HttpStatusCode.OK);
                      }
                  }
                  else
                  {
                      return Request.CreateResponse(HttpStatusCode.NotFound);
                  }
                  return Request.CreateResponse(HttpStatusCode.BadRequest);
              }
          }
      }
      
    5. Enregistrez et fermez le fichier BooksController.cs .

  10. Générez l’application API web pour case activée pour les erreurs.

Étape 2 : Ajout du projet de catalogue bookstore Windows Phone 8

L’étape suivante de ce scénario de bout en bout consiste à créer l’application de catalogue pour Windows Phone 8. Cette application utilise le modèle d’application databound Windows Phone pour l’interface utilisateur par défaut, et elle utilise l’application d’API web que vous avez créée à l’étape 1 de ce didacticiel comme source de données.

  1. Cliquez avec le bouton droit sur la solution BookStore dans l’Explorateur de solutions, puis cliquez sur Ajouter, puis sur Nouveau projet.

  2. Lorsque la boîte de dialogue Nouveau projet s’affiche, développez Installé, Visual C#, puis Windows Phone.

  3. Mettez en surbrillance Windows Phone Application databound, entrez BookCatalog comme nom, puis cliquez sur OK.

  4. Ajoutez le package NuGet Json.NET au projet BookCatalog :

    1. Cliquez avec le bouton droit sur Références pour le projet BookCatalog dans l’Explorateur de solutions, puis cliquez sur Gérer les packages NuGet.
    2. Lorsque la boîte de dialogue Gérer les packages NuGet s’affiche, développez la section En ligne et mettez en surbrillance nuget.org.
    3. Entrez Json.NET dans le champ de recherche, puis cliquez sur l’icône de recherche.
    4. Mettez en surbrillance Json.NET dans les résultats de recherche, puis cliquez sur Installer.
    5. Une fois l’installation terminée, cliquez sur Fermer.
  5. Ajoutez le modèle BookDetails au projet BookCatalog ; il contient un modèle générique de la classe bookstore :

    1. Cliquez avec le bouton droit sur le projet BookCatalog dans l’Explorateur de solutions, cliquez sur Ajouter, puis sur Nouveau dossier.

    2. Nommez le nouveau dossier Modèles.

    3. Cliquez avec le bouton droit sur le dossier Modèles dans l’Explorateur de solutions, cliquez sur Ajouter, puis sur Classe.

    4. Lorsque la boîte de dialogue Ajouter un nouvel élément s’affiche, nommez le fichier de classe BookDetails.cs, puis cliquez sur Ajouter.

    5. Lorsque le fichier BookDetails.cs est ouvert, remplacez le code dans le fichier par les éléments suivants :

      using System;
      using System.Text;
      
      namespace BookCatalog.Models
      {
          /// <summary>
          /// Define a class that will hold the detailed information for a book.
          /// </summary>
          public class BookDetails
          {
              public String Id { get; set; }
              public String Title { get; set; }
              public String Author { get; set; }
              public String Genre { get; set; }
              public Decimal Price { get; set; }
              public DateTime PublishDate { get; set; }
              public String Description { get; set; }
          }
      }
      
    6. Enregistrez et fermez le fichier BookDetails.cs .

  6. Mettez à jour la classe MainViewModel.cs pour inclure la fonctionnalité de communication avec l’application d’API web BookStore :

    1. Développez le dossier ViewModels dans l’Explorateur de solutions, puis double-cliquez sur le fichier MainViewModel.cs .

    2. Lorsque le fichier MainViewModel.cs est ouvert, remplacez le code dans le fichier par ce qui suit : notez que vous devez mettre à jour la valeur de la apiUrl constante avec l’URL réelle de votre API web :

      using System;
      using System.Collections.ObjectModel;
      using System.ComponentModel;
      using System.Net;
      using System.Net.NetworkInformation;
      using BookCatalog.Resources;
      using System.Collections.Generic;
      using Newtonsoft.Json;
      using BookCatalog.Models;
      
      namespace BookCatalog.ViewModels
      {
          public class MainViewModel : INotifyPropertyChanged
          {
              const string apiUrl = @"http://www.contoso.com/api/Books";
      
              public MainViewModel()
              {
                  this.Items = new ObservableCollection<ItemViewModel>();
              }
      
              /// <summary>
              /// A collection for ItemViewModel objects.
              /// </summary>
              public ObservableCollection<ItemViewModel> Items { get; private set; }
      
              public bool IsDataLoaded
              {
                  get;
                  private set;
              }
      
              /// <summary>
              /// Creates and adds a few ItemViewModel objects into the Items collection.
              /// </summary>
              public void LoadData()
              {
                  if (this.IsDataLoaded == false)
                  {
                      this.Items.Clear();
                      this.Items.Add(new ItemViewModel() { ID = "0", LineOne = "Please Wait...", LineTwo = "Please wait while the catalog is downloaded from the server.", LineThree = null });
                      WebClient webClient = new WebClient();
                      webClient.Headers["Accept"] = "application/json";
                      webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadCatalogCompleted);
                      webClient.DownloadStringAsync(new Uri(apiUrl));
                  }
              }
      
              private void webClient_DownloadCatalogCompleted(object sender, DownloadStringCompletedEventArgs e)
              {
                  try
                  {
                      this.Items.Clear();
                      if (e.Result != null)
                      {
                          var books = JsonConvert.DeserializeObject<BookDetails[]>(e.Result);
                          int id = 0;
                          foreach (BookDetails book in books)
                          {
                              this.Items.Add(new ItemViewModel()
                              {
                                  ID = (id++).ToString(),
                                  LineOne = book.Title,
                                  LineTwo = book.Author,
                                  LineThree = book.Description.Replace("\n", " ")
                              });
                          }
                          this.IsDataLoaded = true;
                      }
                  }
                  catch (Exception ex)
                  {
                      this.Items.Add(new ItemViewModel()
                      {
                          ID = "0",
                          LineOne = "An Error Occurred",
                          LineTwo = String.Format("The following exception occured: {0}", ex.Message),
                          LineThree = String.Format("Additional inner exception information: {0}", ex.InnerException.Message)
                      });
                  }
              }
      
              public event PropertyChangedEventHandler PropertyChanged;
              private void NotifyPropertyChanged(String propertyName)
              {
                  PropertyChangedEventHandler handler = PropertyChanged;
                  if (null != handler)
                  {
                      handler(this, new PropertyChangedEventArgs(propertyName));
                  }
              }
          }
      }
      
    3. Enregistrez et fermez le fichier MainViewModel.cs .

  7. Mettez à jour le fichier MainPage.xaml pour personnaliser le nom de l’application :

    1. Double-cliquez sur le fichier MainPage.xaml dans l’Explorateur de solutions.

    2. Lorsque le fichier MainPage.xaml est ouvert, recherchez les lignes de code suivantes :

      <StackPanel Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/> 
          <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
      </StackPanel>
      
    3. Remplacez ces lignes par les éléments suivants :

      <StackPanel Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="Book Store" Style="{StaticResource PhoneTextTitle1Style}"/> 
          <TextBlock Text="Current Catalog" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}"/>
      </StackPanel>
      
    4. Enregistrez et fermez le fichier MainPage.xaml .

  8. Mettez à jour le fichier DetailsPage.xaml pour personnaliser les éléments affichés :

    1. Double-cliquez sur le fichier DetailsPage.xaml dans l’Explorateur de solutions.

    2. Lorsque le fichier DetailsPage.xaml est ouvert, recherchez les lignes de code suivantes :

      <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
          <TextBlock Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
      </StackPanel>
      
    3. Remplacez ces lignes par les éléments suivants :

      <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="Book Store" Style="{StaticResource PhoneTextTitle1Style}"/>
          <TextBlock Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}"/>
      </StackPanel>
      
    4. Enregistrez et fermez le fichier DetailsPage.xaml .

  9. Générez l’application Windows Phone pour case activée en cas d’erreurs.

Étape 3 : Test de la solution de bout en bout

Comme indiqué dans la section Prérequis de ce tutoriel, lorsque vous testez la connectivité entre l’API web et Windows Phone 8 projets sur votre système local, vous devez suivre les instructions de l’article Connexion de l’émulateur Windows Phone 8 aux applications d’API web sur un ordinateur local pour configurer votre environnement de test.

Une fois l’environnement de test configuré, vous devez définir l’application Windows Phone comme projet de démarrage. Pour ce faire, mettez en surbrillance l’application BookCatalog dans l’Explorateur de solutions, puis cliquez sur Définir comme projet de démarrage :

Capture d’écran de la fenêtre de l’Explorateur de solutions, montrant les options de menu, pour définir l’application Windows Phone dans l’option « configurer comme projet de démarrage ».
Cliquez sur l’image pour développer

Lorsque vous appuyez sur F5, Visual Studio démarre l’émulateur Windows Phone, qui affiche un message « Veuillez attendre » pendant que les données d’application sont récupérées à partir de votre API web :

Capture d’écran de la fenêtre de l’Explorateur de solutions, montrant l’émulateur de téléphone s’y afficher, affichant le titre Book Store et le message « Veuillez patienter ».
Cliquez sur l’image pour développer

Si tout réussit, le catalogue doit s’afficher :

Capture d’écran de la fenêtre explorateur de solutions, montrant l’émulateur de téléphone sur celui-ci, affichant le Book Store avec les titres dans le catalogue.
Cliquez sur l’image pour développer

Si vous appuyez sur un titre de livre, l’application affiche la description du livre :

Capture d’écran de l’émulateur de téléphone, sur la fenêtre de l’Explorateur de solutions, affichant un titre et une description du livre.
Cliquez sur l’image pour développer

Si l’application ne peut pas communiquer avec votre API web, un message d’erreur s’affiche :

Capture d’écran de l’émulateur de téléphone, affiché sur la fenêtre de l’Explorateur de solutions, montrant une « erreur qui s’est produite » et une brève description de l’erreur.
Cliquez sur l’image pour développer

Si vous appuyez sur le message d’erreur, tous les détails supplémentaires sur l’erreur s’affichent :

Capture d’écran de l’émulateur de téléphone, sur la boîte de dialogue explorateur de solutions, montrant « Une erreur s’est produite » suivie des détails de l’erreur.
Cliquez sur l’image pour développer