Windows Phone 8 アプリケーションから Web API を呼び出す (C#)

作成者: Robert McMurray

このチュートリアルでは、Windows Phone 8 アプリケーションに書籍のカタログを提供する ASP.NET Web API アプリケーションで構成される完全なエンドツーエンドのシナリオを作成する方法について説明します。

概要

ASP.NET Web APIなどの RESTful サービスでは、サーバー側アプリケーションとクライアント側アプリケーションのアーキテクチャを抽象化することで、開発者向けの HTTP ベースのアプリケーションの作成を簡略化できます。 Web API 開発者は、通信用に独自のソケット ベースのプロトコルを作成する代わりに、アプリケーションに必要な HTTP メソッド (GET、POST、PUT、DELETE など) を公開するだけで済み、クライアント アプリケーション開発者はアプリケーションに必要な HTTP メソッドのみを使用する必要があります。

このエンドツーエンドのチュートリアルでは、Web API を使用して次のプロジェクトを作成する方法について説明します。

前提条件

  • Windows Phone 8 SDK がインストールされているVisual Studio 2013
  • Hyper-V がインストールされている 64 ビット システム上の Windows 8 以降
  • その他の要件の一覧については、Windows Phone SDK 8.0 ダウンロード ページの「システム要件」セクションを参照してください。

Note

ローカル システム上の Web API と Windows Phone 8 プロジェクトの間の接続をテストする場合は、「ローカル コンピューターでの web API アプリケーションへのWindows Phone 8 エミュレーターの接続」の手順に従ってテスト環境を設定する必要があります。

手順 1: Web API Bookstore プロジェクトを作成する

このエンドツーエンドのチュートリアルの最初の手順は、すべての CRUD 操作をサポートする Web API プロジェクトを作成することです。このチュートリアルの手順 2 では、Windows Phone アプリケーション プロジェクトをこのソリューションに追加します。

  1. Visual Studio 2013を開きます。

  2. [ ファイル]、[ 新規作成]、[ プロジェクト] の順にクリックします。

  3. [ 新しいプロジェクト ] ダイアログ ボックスが表示されたら、[ インストール済み]、[ テンプレート]、[ Visual C#]、[ Web] の順に展開します。

    メニューにファイル パスが表示され、新しいプロジェクトを作成する手順が強調表示されている [新しいプロジェクト] ダイアログ ボックスのスクリーンショット。
    画像をクリックして展開する
  4. Web アプリケーション ASP.NET 強調表示し、プロジェクト名として「BookStore」と入力し、[OK] をクリックします

  5. [ 新しい ASP.NET プロジェクト ] ダイアログ ボックスが表示されたら、 Web API テンプレートを選択し、[OK] をクリック します

    [A S P dot NET project bookstore] ダイアログ ボックスのスクリーンショット。テンプレート オプションとテンプレート フォルダーとコア参照を選択するためのチェック ボックスが表示されています。
    画像をクリックして展開する
  6. Web API プロジェクトが開いたら、プロジェクトからサンプル コントローラーを削除します。

    1. ソリューション エクスプローラーで Controllers フォルダーを展開します。
    2. ValuesController.cs ファイルを右クリックし、[削除] をクリックします
    3. 削除を確認するメッセージが表示されたら 、[OK] をクリックします
  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. Bookstore モデルを 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. Bookstore コントローラーを Web API プロジェクトに追加します。

    1. ソリューション エクスプローラーで Controllers フォルダーを右クリックし、[ 追加] をクリックし、[ コントローラー] をクリックします。

    2. [ スキャフォールディングの追加 ] ダイアログ ボックスが表示されたら、[ 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 App テンプレートを使用し、このチュートリアルの手順 1 で作成した Web API アプリケーションをデータ ソースとして使用します。

  1. ソリューション エクスプローラーで BookStore ソリューションを右クリックし、[ 追加]、[ 新しいプロジェクト] の順にクリックします。

  2. [新しいプロジェクト] ダイアログ ボックスが表示されたら、[インストール済み]、[Visual C#]、[Windows Phone] の順に展開します。

  3. Databound App Windows Phone強調表示し、名前として「BookCatalog」と入力し、[OK] をクリックします

  4. Json.NET NuGet パッケージを BookCatalog プロジェクトに追加します。

    1. ソリューション エクスプローラーで BookCatalog プロジェクトの [参照] を右クリックし、[NuGet パッケージの管理] をクリックします。
    2. [ NuGet パッケージの管理 ] ダイアログ ボックスが表示されたら、[ オンライン ] セクションを展開し、[ nuget.org] を強調表示します。
    3. 検索フィールド に「Json.NET 」と入力し、検索アイコンをクリックします。
    4. 検索結果 で Json.NET を強調表示し、[ インストール] をクリックします。
    5. インストールが完了したら、[ 閉じる] をクリックします。
  5. BookDetails モデルを BookCatalog プロジェクトに追加します。これには、bookstore クラスのジェネリック モデルが含まれています。

    1. ソリューション エクスプローラーで BookCatalog プロジェクトを右クリックし、[ 追加] をクリックし、[ 新しいフォルダー] をクリックします。

    2. 新しいフォルダーに Models という名前を付けます。

    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 の実際の apiUrl URL で更新する必要があることに注意してください。

      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 Emulator to Web API Applications の接続」の手順に従ってテスト環境を設定する必要があります。

テスト環境を構成したら、Windows Phone アプリケーションをスタートアップ プロジェクトとして設定する必要があります。 これを行うには、ソリューション エクスプローラーで BookCatalog アプリケーションを強調表示し、[ スタートアップ プロジェクトとして設定] をクリックします。

[スタートアップ プロジェクトとして設定] オプションで Windows Phone アプリケーションを設定するためのメニュー オプションを示すソリューション エクスプローラー ウィンドウのスクリーンショット。
画像をクリックして展開する

F5 キーを押すと、Visual Studio は両方の Windows Phone Emulator を起動します。これにより、アプリケーション データが Web API から取得される間に "Please Wait" というメッセージが表示されます。

ソリューション エクスプローラー ウィンドウのスクリーンショット。電話エミュレーターがポップアップ表示され、タイトルの Book Store と
画像をクリックして展開する

すべてが成功すると、カタログが表示されます。

ソリューション エクスプローラー ウィンドウのスクリーンショット。電話エミュレーターが表示され、カタログにタイトルが表示された Book Store が表示されています。
画像をクリックして展開する

本のタイトルをタップすると、アプリケーションに書籍の説明が表示されます。

電話エミュレーターのスクリーンショット。ソリューション エクスプローラー ウィンドウの上に、書籍のタイトルと説明が表示されています。
画像をクリックして展開する

アプリケーションが Web API と通信できない場合は、次のエラー メッセージが表示されます。

ソリューション エクスプローラー ウィンドウに表示される電話エミュレーターのスクリーンショット。
画像をクリックして展開する

エラー メッセージをタップすると、エラーに関する追加の詳細が表示されます。

電話エミュレーターのスクリーンショット。ソリューション エクスプローラーのダイアログ ボックスで、
画像をクリックして展開する