共用方式為


從 Windows Phone 8 應用程式呼叫 Web API (C#)

演講者:Robert McMurray

在本教學課程中,您將學習如何建立一個完整的端對端場景,包括一個 ASP.NET Web API 應用程式,該應用程式向 Windows Phone 8 應用程式提供書籍目錄。

概觀

ASP.NET Web API 等 RESTful 服務會透過抽象伺服器端和用戶端應用程式的架構,簡化開發人員建立 HTTP 型應用程式的過程。 Web API 開發人員無需建立以通訊端為基礎的專有通訊協定,只需為其應用程式發布必需的 HTTP 方法 (例如:GET、POST、PUT、DELETE),而用戶端應用程式開發人員只需使用其應用程式所需的方法。

在本端對端教學課程中,您將學習如何使用 Web API 建立以下專案:

必要條件

  • 已安裝 Windows Phone 8 SDK 的 Visual Studio 2013
  • 已安裝 Hyper-V 的 64 位元系統上的 Windows 8 或更高版本
  • 有關其他要求的清單,請參閱 Windows Phone SDK 8.0 下載頁面上的系統需求部分。

注意

如果您要在本機系統上測試 Web API 和 Windows Phone 8 專案之間的連接,則需要按照「將 Windows Phone 8 仿真器連接到本機電腦上的 Web API 應用程式」一文中的說明來設定測試環境。

步驟 1:建立 Web API 書店專案

這個端對端教學課程的第一步是建立一個支援所有 CRUD 操作的 Web API 專案;請注意,您將在本教學課程的步驟 2 中將 Windows Phone 應用程式專案新增至此解決方案。

  1. 開啟 Visual Studio 2013

  2. 按一下「檔案」,然後按一下「新增」,然後按一下「專案」。

  3. 顯示「新專案」對話方塊時,依序展開「已安裝」、「範本」、「Visual C#」和「Web」。

    「新專案」對話方塊的螢幕擷取畫面,顯示功能表中的檔案路徑並醒目顯示建立新專案的步驟。
    按一下圖片可展開
  4. 醒目顯示 ASP.NET Web 應用程式,輸入 BookStore 作為專案名稱,然後按一下「確定」。

  5. 顯示「新 ASP.NET 專案」對話方塊時,選擇 Web API 範本,然後按一下「確定」。

    ASP dot NET 專案書店對話方塊的螢幕擷取畫面,顯示範本選項以及用於選擇範本資料夾和核心參考的核取方塊。
    按一下圖片可展開
  6. 當 Web API 專案開啟時,從專案中移除範例控制器:

    1. 展開方案總管中的 Controllers 資料夾。
    2. 以滑鼠右鍵按一下 ValuesController.cs 檔案,然後按一下「刪除」。
    3. 當提示確認刪除時,按一下「確定」。
  7. 將 XML 資料檔加入 Web API 專案;該文件包含書店目錄的內容:

    1. 以滑鼠右鍵按一下方案總管中的 App_Data 資料夾,然後按一下「新增」,然後按一下「新專案」。

    2. 顯示「新專案」對話方塊時,醒目顯示「XML 檔案」範本。

    3. 將檔案命名為 Books.xml,然後按一下「新增」。

    4. 開啟 Books.xml 檔案後,將檔案中的程式碼替換為 MSDN 上範例 books.xml 檔案中的 XML:

      <?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. 儲存並關閉 XML 檔案。

  8. 將書店模型加入 Web API 專案中;此模型包含書店應用程式的建立、讀取、更新和刪除 (CRUD) 邏輯:

    1. 以滑鼠右鍵按一下方案總管中的 Models 資料夾,然後按一下「新增」,然後按一下「類別」。

    2. 顯示「新增項目」對話方塊時,將類別檔案命名為 BookDetails.cs,然後按一下「新增」。

    3. 開啟 BookDetails.cs 檔案後,將檔案中的程式碼替換為以下內容:

      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. 儲存並關閉 BookDetails.cs 檔案。

  9. 將書店控制器新增至 Web API 專案:

    1. 以滑鼠右鍵按一下方案總管中的 Controllers 資料夾,然後按一下「新增」,然後按一下「控制器」。

    2. 顯示「新增 Scaffold」對話方塊時,醒目顯示「Web API 2 控制器 - 空白」,然後按一下「新增」。

    3. 顯示「新增控制器」對話方塊時,將控制器命名為 BooksController,然後按一下「新增」。

    4. 打開 BooksController.cs 檔案後,將檔案中的程式碼替換為以下內容:

      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. 儲存並關閉 BooksController.cs 檔案。

  10. 建立 Web API 應用程式以檢查錯誤。

步驟 2:新增 Windows Phone 8 書店目錄專案

這個端對端情境的下一步是為 Windows Phone 8 建立目錄應用程式。 這個應用程式將使用 Windows Phone Databound 應用程式範本作為預設使用者介面,並將使用您在本教學課程的步驟 1 中建立的 Web API 應用程式作為資料來源。

  1. 以滑鼠右鍵按一下方案總管中的 BookStore 解決方案,然後按一下「新增」,然後按一下「新專案」。

  2. 顯示「新專案」對話方塊時,依序展開「已安裝」、「Visual C#」和「Windows Phone」。

  3. 醒目顯示 Windows Phone Databound 應用程式,輸入 BookCatalog 作為名稱,然後按一下「確定」。

  4. 將 Json.NET NuGet 套件新增至 BookCatalog 專案:

    1. 在方案總管中以滑鼠右鍵按一下 BookCatalog 專案的「參考」,然後按一下「管理 NuGet 套件」。
    2. 顯示「管理 NuGet 套件」對話方塊時,展開「線上」區段,並醒目顯示 nuget.org
    3. 在搜尋欄位中輸入 Json.NET,然後按一下搜尋圖示。
    4. 在搜尋結果中醒目顯示 Json.NET,然後按一下「安裝」。
    5. 安裝完成後,按一下「關閉」。
  5. BookDetails 模型加入 BookCatalog 專案中;這包含書店類別的一般模型:

    1. 在方案總管中以滑鼠右鍵按一下 BookCatalog 專案,然後按一下「新增」,然後按一下「新資料夾」。

    2. 將新的資料夾命名為模型

    3. 以滑鼠右鍵按一下方案總管中的 Models 資料夾,然後按一下「新增」,然後按一下「類別」。

    4. 顯示「新增項目」對話方塊時,將類別檔案命名為 BookDetails.cs,然後按一下「新增」。

    5. 開啟 BookDetails.cs 檔案後,將檔案中的程式碼替換為以下內容:

      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. 儲存並關閉 BookDetails.cs 檔案。

  6. 更新 MainViewModel.cs 類別以包含與 BookStore Web API 應用程式通訊的功能:

    1. 在方案總管中展開 ViewModels 資料夾,然後按兩下 MainViewModel.cs 檔案。

    2. 開啟 MainViewModel.cs 檔案後,將檔案中的程式碼替換為以下內容;請注意,您需要使用 Web API 的實際 URL 更新 apiUrl 常數的值:

      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. 儲存並關閉 MainViewModel.cs 檔案。

  7. 更新 MainPage.xaml 檔案以自訂應用程式名稱:

    1. 按兩下方案總管中的 MainPage.xaml 檔案。

    2. 開啟 MainPage.xaml 檔案後,找到以下幾行程式碼:

      <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. 將這些行替換為以下內容:

      <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. 儲存並關閉 MainPage.xaml 檔案。

  8. 更新 DetailsPage.xaml 檔案以自訂顯示的項目:

    1. 按兩下方案總管中的 DetailsPage.xaml 檔案。

    2. 開啟 DetailsPage.xaml 檔案後,找到以下幾行程式碼:

      <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. 將這些行替換為以下內容:

      <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. 儲存並關閉 DetailsPage.xaml 檔案。

  9. 建立 Windows Phone 應用程式以檢查錯誤。

第 3 步:測試端對端解決方案

如本教學課程的先決條件部分所述,當您在本機系統上測試 Web API 和 Windows Phone 8 專案之間的連接時,您需要按照「將 Windows Phone 8 仿真器連接到電腦上的 Web API 應用程式」一文中的說明進行操作。

設定測試環境後,您需要將 Windows Phone 應用程式設定為啟動專案。 若要這樣做,請在方案總管中醒目顯示 BookCatalog 應用程式,然後按一下「設定為啟動專案」:

方案總管視窗的螢幕擷取畫面,顯示功能表選項,用於在「設定為啟動專案」選項中設定 Windows Phone 應用程式。
按一下圖片可展開

當您按 F5 時,Visual Studio 將啟動 Windows Phone 仿真器,在從 Web API 擷取應用程式資料時,它將顯示「請稍候」訊息:

方案總管視窗的螢幕擷取畫面,顯示手機仿真器在其上彈出,顯示標題 Book Store 和「請稍候」訊息。
按一下圖片可展開

如果一切順利,您應該會看到顯示的目錄:

方案總管視窗的螢幕擷取畫面,顯示了其上方的手機仿真器,顯示了 Book Store 以及目錄中的標題。
按一下圖片可展開

如果您點擊任何書名,應用程式將顯示該書籍的描述:

手機仿真器的螢幕擷取畫面,位於方案總管視窗上方,顯示書名和描述。
按一下圖片可展開

如果應用程式無法與您的 Web API 通訊,將顯示錯誤訊息:

電話仿真器的螢幕擷取畫面,顯示在方案總管視窗上,顯示「發生錯誤」以及錯誤的簡短描述。
按一下圖片可展開

如果您點擊錯誤訊息,將顯示有關該錯誤的任何其他詳細資訊:

手機仿真器的螢幕擷取畫面,位於方案總管對話方塊中,顯示「發生錯誤」,後面接著錯誤的詳細資訊。
按一下圖片可展開