Chamar a API Web em um aplicativo do Windows Phone 8 (C#)

por Robert McMurray

Neste tutorial, você aprenderá a criar um cenário completo de ponta a ponta que consiste em um aplicativo ASP.NET Web API que fornece um catálogo de livros para um aplicativo Windows Phone 8.

Visão geral

Serviços RESTful como ASP.NET Web API simplificam a criação de aplicativos baseados em HTTP para desenvolvedores abstraindo a arquitetura para aplicativos do lado do servidor e do lado do cliente. Em vez de criar um protocolo proprietário baseado em soquete para comunicação, os desenvolvedores de API Web simplesmente precisam publicar os métodos HTTP necessários para seu aplicativo (por exemplo: GET, POST, PUT, DELETE) e os desenvolvedores de aplicativos cliente só precisam consumir os métodos HTTP necessários para seu aplicativo.

Neste tutorial de ponta a ponta, você aprenderá a usar a API Web para criar os seguintes projetos:

Pré-requisitos

  • Visual Studio 2013 com o SDK do Windows Phone 8 instalado
  • Windows 8 ou posterior em um sistema de 64 bits com o Hyper-V instalado
  • Para obter uma lista de requisitos adicionais, consulte a seção Requisitos do sistema na página de download do Windows Phone SDK 8.0.

Observação

Se você vai testar a conectividade entre a API Web e Windows Phone 8 projetos em seu sistema local, você precisará seguir as instruções no artigo Conectando o Emulador do Windows Phone 8 aos Aplicativos de API Web em um Computador Local para configurar seu ambiente de teste.

Etapa 1: Criando o projeto de livraria de API Web

A primeira etapa deste tutorial de ponta a ponta é criar um projeto de API Web que dê suporte a todas as operações CRUD; observe que você adicionará o projeto de aplicativo Windows Phone a essa solução na Etapa 2 deste tutorial.

  1. Abra Visual Studio 2013.

  2. Clique em Arquivo, em seguida, Novo e, em seguida, Projeto.

  3. Quando a caixa de diálogo Novo Projeto for exibida, expanda Instalado, Modelos, Visual C#e Web.

    Captura de tela da caixa de diálogo
    Clique em imagem para expandir
  4. Realce ASP.NET Aplicativo Web, insira BookStore para o nome do projeto e clique em OK.

  5. Quando a caixa de diálogo Novo projeto ASP.NET for exibida, selecione o modelo de API Web e clique em OK.

    Captura de tela da caixa de diálogo de livraria do projeto A SP dot NET, mostrando opções de modelo e caixas de marcar para selecionar a pasta de modelo e a referência principal.
    Clique em imagem para expandir
  6. Quando o projeto de API Web for aberto, remova o controlador de exemplo do projeto:

    1. Expanda a pasta Controladores no gerenciador de soluções.
    2. Clique com o botão direito do mouse no arquivo ValuesController.cs e clique em Excluir.
    3. Clique em OK quando solicitado a confirmar a exclusão.
  7. Adicionar um arquivo de dados XML ao projeto da API Web; este arquivo contém o conteúdo do catálogo de livrarias:

    1. Clique com o botão direito do mouse na pasta App_Data no gerenciador de soluções, clique em Adicionar e clique em Novo Item.

    2. Quando a caixa de diálogo Adicionar Novo Item for exibida, realce o modelo arquivo XML .

    3. Nomeie o arquivo Books.xmle clique em Adicionar.

    4. Quando o arquivo Books.xml for aberto, substitua o código no arquivo pelo XML do arquivo debooks.xml de exemplo no 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. Salve e feche o arquivo XML.

  8. Adicione o modelo de livraria ao projeto da API Web; esse modelo contém a lógica CRUD (Criar, Ler, Atualizar e Excluir) para o aplicativo de livraria:

    1. Clique com o botão direito do mouse na pasta Modelos no gerenciador de soluções, clique em Adicionar e, em seguida, clique em Classe.

    2. Quando a caixa de diálogo Adicionar Novo Item for exibida, nomeie o arquivo de classe BookDetails.cs e clique em Adicionar.

    3. Quando o arquivo BookDetails.cs for aberto, substitua o código no arquivo pelo seguinte:

      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. Salve e feche o arquivo BookDetails.cs .

  9. Adicione o controlador de livraria ao projeto da API Web:

    1. Clique com o botão direito do mouse na pasta Controladores no gerenciador de soluções, clique em Adicionar e, em seguida, clique em Controlador.

    2. Quando a caixa de diálogo Adicionar Scaffold for exibida, realce o Controlador da API Web 2 – Vazio e clique em Adicionar.

    3. Quando a caixa de diálogo Adicionar Controlador for exibida, nomeie o controlador BooksController e clique em Adicionar.

    4. Quando o arquivo BooksController.cs for aberto, substitua o código no arquivo pelo seguinte:

      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. Salve e feche o arquivo BooksController.cs .

  10. Crie o aplicativo de API Web para marcar para erros.

Etapa 2: Adicionar o projeto de catálogo de livrarias do Windows Phone 8

A próxima etapa desse cenário de ponta a ponta é criar o aplicativo de catálogo para Windows Phone 8. Esse aplicativo usará o modelo Windows Phone Aplicativo de Databound para a interface do usuário padrão e usará o aplicativo de API Web que você criou na Etapa 1 deste tutorial como fonte de dados.

  1. Clique com o botão direito do mouse na solução BookStore no no gerenciador de soluções e clique em Adicionar e em Novo Projeto.

  2. Quando a caixa de diálogo Novo Projeto for exibida, expanda Instalado, Visual C#e, em seguida, Windows Phone.

  3. Realce Windows Phone Aplicativo de Databound, insira BookCatalog para o nome e clique em OK.

  4. Adicione o Json.NET pacote NuGet ao projeto BookCatalog :

    1. Clique com o botão direito do mouse em Referências para o projeto BookCatalog no gerenciador de soluções e clique em Gerenciar Pacotes NuGet.
    2. Quando a caixa de diálogo Gerenciar Pacotes NuGet for exibida, expanda a seção Online e realce nuget.org.
    3. Insira Json.NET no campo de pesquisa e clique no ícone de pesquisa.
    4. Realce Json.NET nos resultados da pesquisa e clique em Instalar.
    5. Quando a instalação for concluída, clique em Fechar.
  5. Adicione o modelo BookDetails ao projeto BookCatalog ; isso contém um modelo genérico da classe bookstore:

    1. Clique com o botão direito do mouse no projeto BookCatalog no gerenciador de soluções, clique em Adicionar e clique em Nova Pasta.

    2. Nomeie a nova pasta Modelos.

    3. Clique com o botão direito do mouse na pasta Modelos no gerenciador de soluções, clique em Adicionar e, em seguida, clique em Classe.

    4. Quando a caixa de diálogo Adicionar Novo Item for exibida, nomeie o arquivo de classe BookDetails.cs e clique em Adicionar.

    5. Quando o arquivo BookDetails.cs for aberto, substitua o código no arquivo pelo seguinte:

      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. Salve e feche o arquivo BookDetails.cs .

  6. Atualize a classe MainViewModel.cs para incluir a funcionalidade para se comunicar com o aplicativo de API Web BookStore:

    1. Expanda a pasta ViewModels no gerenciador de soluções e clique duas vezes no arquivo MainViewModel.cs .

    2. Quando o arquivo MainViewModel.cs for aberto, substitua o código no arquivo pelo seguinte; observe que você precisará atualizar o valor da apiUrl constante com a URL real da 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. Salve e feche o arquivo MainViewModel.cs .

  7. Atualize o arquivo MainPage.xaml para personalizar o nome do aplicativo:

    1. Clique duas vezes no arquivo MainPage.xaml no gerenciador de soluções.

    2. Quando o arquivo MainPage.xaml for aberto, localize as seguintes linhas 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. Substitua essas linhas pelo seguinte:

      <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. Salve e feche o arquivo MainPage.xaml .

  8. Atualize o arquivo DetailsPage.xaml para personalizar os itens exibidos:

    1. Clique duas vezes no arquivo DetailsPage.xaml no gerenciador de soluções.

    2. Quando o arquivo DetailsPage.xaml for aberto, localize as seguintes linhas 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. Substitua essas linhas pelo seguinte:

      <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. Salve e feche o arquivo DetailsPage.xaml .

  9. Crie o aplicativo Windows Phone para marcar erros.

Etapa 3: Testar a solução de ponta a ponta

Conforme mencionado na seção Pré-requisitos deste tutorial, ao testar a conectividade entre a API Web e Windows Phone 8 projetos em seu sistema local, você precisará seguir as instruções no artigo Conectando o Emulador do Windows Phone 8 aos Aplicativos de API Web em um computador local para configurar seu ambiente de teste.

Depois de configurar o ambiente de teste, você precisará definir o aplicativo Windows Phone como o projeto de inicialização. Para fazer isso, realce o aplicativo BookCatalog no gerenciador de soluções e clique em Definir como Projeto de Inicialização:

Captura de tela da janela do gerenciador de soluções, mostrando as opções de menu, para definir o aplicativo windows phone na opção
Clique em imagem para expandir

Quando você pressionar F5, o Visual Studio iniciará o emulador Windows Phone, que exibirá uma mensagem "Aguarde" enquanto os dados do aplicativo são recuperados da API Web:

Captura de tela da janela do gerenciador de soluções, mostrando o emulador de telefone aparecendo sobre ele, exibindo o título Repositório de Livros e a mensagem
Clique em imagem para expandir

Se tudo for bem-sucedido, você deverá ver o catálogo exibido:

Captura de tela da janela do gerenciador de soluções, mostrando o emulador de telefone sobre ele, exibindo o Repositório de Livros com os títulos no catálogo.
Clique em imagem para expandir

Se você tocar em qualquer título do livro, o aplicativo exibirá a descrição do livro:

Captura de tela do emulador de telefone, na janela do gerenciador de soluções, exibindo um título e uma descrição do livro.
Clique em imagem para expandir

Se o aplicativo não puder se comunicar com sua API Web, uma mensagem de erro será exibida:

Captura de tela do emulador de telefone, exibida na janela do gerenciador de soluções, mostrando um
Clique em imagem para expandir

Se você tocar na mensagem de erro, todos os detalhes adicionais sobre o erro serão exibidos:

Captura de tela do emulador de telefone, na caixa de diálogo gerenciador de soluções, mostrando
Clique em imagem para expandir