Llamar a Web API desde una aplicación de Windows Phone 8 (C#)

por Robert McMurray

En este tutorial, aprenderá a crear un escenario completo de un extremo a otro que consiste en una aplicación ASP.NET Web API que proporciona un catálogo de libros para una aplicación Windows Phone 8.

Información general

Los servicios RESTful como ASP.NET Web API simplifican la creación de aplicaciones basadas en HTTP para desarrolladores mediante la abstracción de la arquitectura para las aplicaciones del lado servidor y del lado cliente. En lugar de crear un protocolo basado en sockets propietario para la comunicación, los desarrolladores de la API Web simplemente necesitan publicar los métodos HTTP necesarios para su aplicación (por ejemplo: GET, POST, PUT, DELETE) y los desarrolladores de aplicaciones cliente solo necesitan consumir los métodos HTTP necesarios para su aplicación.

En este tutorial de un extremo a otro, aprenderá a usar la API Web para crear los siguientes proyectos:

Requisitos previos

  • Visual Studio 2013 con el SDK de Windows Phone 8 instalado
  • Windows 8 o posterior en un sistema de 64 bits con Hyper-V instalado
  • Para obtener una lista de requisitos adicionales, consulte la sección requisitos del sistema en la página de descarga del SDK de Windows Phone 8,0 .

Note

Si va a probar la conectividad entre los proyectos de API Web y Windows Phone 8 en el sistema local, deberá seguir las instrucciones del artículo conexión del emulador de Windows Phone 8 a aplicaciones de API Web en un equipo local para configurar el entorno de pruebas.

Paso 1: creación del proyecto de librería de API Web

El primer paso de este tutorial de un extremo a otro es crear un proyecto de API Web que admita todas las operaciones CRUD; Tenga en cuenta que agregará el proyecto de aplicación de Windows Phone a esta solución en el paso 2 de este tutorial.

  1. Abra Visual Studio 2013.

  2. Haga clic en archivo, nuevoy proyecto.

  3. Cuando se muestre el cuadro de diálogo nuevo proyecto , expanda instalado, plantillas, Visual C# y, a continuación, Web.

    Haga clic en la imagen para expandir
  4. Resalte la aplicación Web ASP.net, escriba BookStore como nombre del proyecto y, a continuación, haga clic en Aceptar.

  5. Cuando se muestre el cuadro de diálogo nuevo proyecto ASP.net , seleccione la plantilla API Web y, a continuación, haga clic en Aceptar.

    Haga clic en la imagen para expandir
  6. Cuando se abra el proyecto de API Web, quite el controlador de ejemplo del proyecto:

    1. Expanda la carpeta Controladores en el explorador de soluciones.
    2. Haga clic con el botón secundario en el archivo ValuesController.CS y, a continuación, haga clic en eliminar.
    3. Haga clic en Aceptar cuando se le pida que confirme la eliminación.
  7. Agregar un archivo de datos XML al proyecto de API Web; este archivo incluye el contenido del catálogo de la librería:

    1. Haga clic con el botón secundario en la carpeta App_Data del explorador de soluciones, haga clic en Agregary, a continuación, haga clic en nuevo elemento.

    2. Cuando se muestre el cuadro de diálogo Agregar nuevo elemento , resalte la plantilla de archivo XML .

    3. Asigne al archivo el nombre books. XMLy, a continuación, haga clic en Agregar.

    4. Cuando se abra el archivo books. XML , reemplace el código del archivo por el XML del archivo books. XML de ejemplo en 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. Guarde y cierre el archivo XML.

  8. Agregue el modelo Bookstore al proyecto de API Web. Este modelo contiene la lógica de creación, lectura, actualización y eliminación (CRUD) de la aplicación Bookstore:

    1. Haga clic con el botón secundario en la carpeta modelos en el explorador de soluciones, haga clic en Agregary, a continuación, en clase.

    2. Cuando se muestre el cuadro de diálogo Agregar nuevo elemento , asigne un nombre al archivo de clase BookDetails.CSy, a continuación, haga clic en Agregar.

    3. Cuando se abra el archivo BookDetails.CS , reemplace el código del archivo por lo siguiente:

      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. Guarde y cierre el archivo BookDetails.CS .

  9. Agregue el controlador de la librería al proyecto de API Web:

    1. Haga clic con el botón secundario en la carpeta Controllers del explorador de soluciones, haga clic en Agregary, a continuación, en controlador.

    2. Cuando aparezca el cuadro de diálogo Agregar scaffold , resalte el controlador de Web API 2-vacíoy, a continuación, haga clic en Agregar.

    3. Cuando se muestre el cuadro de diálogo Agregar controlador , asigne al controlador el nombre BooksControllery, a continuación, haga clic en Agregar.

    4. Cuando se abra el archivo BooksController.CS , reemplace el código del archivo por lo siguiente:

      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. Guarde y cierre el archivo BooksController.CS .

  10. Cree la aplicación de API Web para comprobar si hay errores.

Paso 2: agregar el proyecto de catálogo de la librería de Windows Phone 8

El siguiente paso de este escenario de un extremo a otro es crear la aplicación de catálogo para Windows Phone 8. Esta aplicación usará el Windows Phone plantilla de aplicación de enlace de datos para la interfaz de usuario predeterminada y usará la aplicación de API Web que creó en el paso 1 de este tutorial como origen de datos.

  1. Haga clic con el botón secundario en la solución BookStore en el explorador de soluciones, haga clic en Agregary, a continuación, en nuevo proyecto.

  2. Cuando se muestre el cuadro de diálogo nuevo proyecto , expanda instalado, luego Visual C# y, a continuación, Windows Phone.

  3. Resalte Windows Phone aplicación DataBound, escriba BookCatalog para el nombre y, a continuación, haga clic en Aceptar.

  4. Agregue el paquete NuGet Json.NET al proyecto BookCatalog :

    1. Haga clic con el botón derecho en referencias para el proyecto BookCatalog en el explorador de soluciones y, a continuación, haga clic en administrar paquetes NuGet.
    2. Cuando aparezca el cuadro de diálogo administrar paquetes NuGet , expanda la sección en línea y resalte Nuget.org.
    3. Escriba JSON.net en el campo de búsqueda y haga clic en el icono de búsqueda.
    4. Resalte JSON.net en los resultados de la búsqueda y, a continuación, haga clic en instalar.
    5. Una vez finalizada la instalación, haga clic en cerrar.
  5. Agregue el modelo BookDetails al proyecto BookCatalog ; contiene un modelo genérico de la clase Bookstore:

    1. Haga clic con el botón secundario en el proyecto BookCatalog en el explorador de soluciones, haga clic en Agregary, a continuación, en nueva carpeta.

    2. Asigne un nombre a los nuevos modelosde carpeta.

    3. Haga clic con el botón secundario en la carpeta modelos en el explorador de soluciones, haga clic en Agregary, a continuación, en clase.

    4. Cuando se muestre el cuadro de diálogo Agregar nuevo elemento , asigne un nombre al archivo de clase BookDetails.CSy, a continuación, haga clic en Agregar.

    5. Cuando se abra el archivo BookDetails.CS , reemplace el código del archivo por lo siguiente:

      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. Guarde y cierre el archivo BookDetails.CS .

  6. Actualice la clase MainViewModel.CS para incluir la funcionalidad para comunicarse con la aplicación de API Web de BookStore:

    1. Expanda la carpeta ViewModels en el explorador de soluciones y, a continuación, haga doble clic en el archivo MainViewModel.CS .

    2. Cuando se abra el archivo MainViewModel.CS , reemplace el código del archivo por lo siguiente: Tenga en cuenta que tendrá que actualizar el valor de la constante apiUrl con la dirección URL real de la 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. Guarde y cierre el archivo MainViewModel.CS .

  7. Actualice el archivo mainpage. Xaml para personalizar el nombre de la aplicación:

    1. Haga doble clic en el archivo mainpage. Xaml en el explorador de soluciones.

    2. Cuando se abra el archivo mainpage. Xaml , busque las siguientes líneas de código:

      <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. Reemplace esas líneas por lo siguiente:

      <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. Guarde y cierre el archivo mainpage. Xaml .

  8. Actualice el archivo DetailsPage. Xaml para personalizar los elementos mostrados:

    1. Haga doble clic en el archivo DetailsPage. Xaml en el explorador de soluciones.

    2. Cuando se abra el archivo DetailsPage. Xaml , busque las siguientes líneas de código:

      <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. Reemplace esas líneas por lo siguiente:

      <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. Guarde y cierre el archivo DetailsPage. Xaml .

  9. Compile la aplicación de Windows Phone para comprobar si hay errores.

Paso 3: probar la solución de un extremo a otro

Como se mencionó en la sección de requisitos previos de este tutorial, al probar la conectividad entre la API Web y los proyectos de Windows Phone 8 en el sistema local, deberá seguir las instrucciones del artículo conexión del emulador de Windows Phone 8 a aplicaciones de API Web en un equipo local para configurar el entorno de pruebas.

Una vez configurado el entorno de pruebas, tendrá que establecer la aplicación Windows Phone como el proyecto de inicio. Para ello, resalte la aplicación BookCatalog en el explorador de soluciones y, a continuación, haga clic en establecer como proyecto de inicio:

Haga clic en la imagen para expandir

Al presionar F5, Visual Studio iniciará el emulador de Windows Phone, que mostrará una "espere" mensaje mientras se recuperan los datos de la aplicación de la API Web:

Haga clic en la imagen para expandir

Si todo se realiza correctamente, debería ver el catálogo mostrado:

Haga clic en la imagen para expandir

Si puntea en cualquier título de libro, la aplicación mostrará la descripción del libro:

Haga clic en la imagen para expandir

Si la aplicación no puede comunicarse con la API Web, se mostrará un mensaje de error:

Haga clic en la imagen para expandir

Si puntea en el mensaje de error, se mostrarán los detalles adicionales sobre el error:

Haga clic en la imagen para expandir