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 プロジェクトの間の接続をテストする場合は、「ローカル コンピューターでの Windows Phone 8 Emulator から Web API アプリケーションへの接続」の手順に従ってテスト環境を設定する必要があります。

手順 1: Web API Bookstore プロジェクトの作成

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

  1. Visual Studio 2013 を開きます。

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

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

    Screenshot of the 'new project' dialog box, showing the file path in the menu and highlighting the steps to create the new project.
    画像をクリックして展開
  4. ASP.NET Web アプリケーションを強調表示し、プロジェクト名として「BookStore」と入力し、[OK] をクリックします。

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

    Screenshot of the A S P dot NET project bookstore dialog box, showing template options and check boxes to select template folder and core reference.
    画像をクリックして展開
  6. Web API プロジェクトが開いたら、プロジェクトからサンプル コントローラーを削除します。

    1. ソリューション エクスプローラーで Controllers フォルダーを展開します。
    2. ValuesController.cs ファイルを右クリックし、[削除] をクリックします。
    3. 削除するかどうかを確認するメッセージが表示されたら、[OK] をクリックします。
  7. Web API プロジェクトに XML データ ファイルを追加します。このファイルには、書店カタログの内容が含まれています。

    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 プロジェクトに追加します。このモデルには、Bookstore アプリケーションの作成、読み取り、更新、および削除 (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 データバインド アプリ テンプレートを使用し、このチュートリアルの手順 1 で作成した Web API アプリケーションをデータ ソースとして使用します。

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

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

  3. [Windows Phone データバインド アプリ] を強調表示し、名前に「BookCatalog」を入力して、[OK] をクリックします。

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

    1. ソリューション エクスプローラーで BookCatalog プロジェクトの [参照] を右クリックし、[NuGet パッケージの管理] をクリックします。
    2. [NuGet パッケージの管理] ダイアログ ボックスが表示されたら、[オンライン] セクションを展開し、nuget.org を強調表示します。
    3. 検索フィールドに「Json.NET」と入力し、検索アイコンをクリックします。
    4. 検索結果の Json.NET を強調表示し、[インストール] をクリックします。
    5. インストールが完了したら、[閉じる] をクリックします。
  5. BookCatalog プロジェクトに BookDetails モデルを追加します。これには、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. BookStore Web API アプリケーションと通信する機能を含むように、MainViewModel.cs クラスを更新します。

    1. ソリューション エクスプローラーで ViewModels フォルダーを展開し、MainViewModel.cs ファイルをダブルクリックします。

    2. MainViewModel.cs ファイルが開いたら、ファイル内のコードを次のコードに置き換えます。apiUrl 定数の値を Web API の実際の 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 から Web API アプリケーションへの接続」の手順に従ってテスト環境を設定する必要があります。

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

Screenshot of the solution explorer window, showing the menu options, to set the windows phone application in the 'set as start up project' option.
画像をクリックして展開

F5 キーを押すと、Visual Studio によって Windows Phone Emulator が起動され、Web API からアプリケーション データが取得されている間に "お待ちください" メッセージが表示されます。

Screenshot of the solution explorer window, showing the phone emulator pop up over it, displaying the title Book Store and the 'please wait' message.
画像をクリックして展開

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

Screenshot of the solution explorer window, showing the phone emulator over it, displaying the Book Store with the titles in the catalog.
画像をクリックして展開

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

Screenshot of the phone emulator, over the solution explorer window, displaying a book title and description.
画像をクリックして展開

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

Screenshot of the phone emulator, displayed over the solution explorer window, showing an 'An Error Occurred' and a brief description of the error.
画像をクリックして展開

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

Screenshot of the phone emulator, over the solution explorer dialog box, showing 'An Error Occurred' followed by details of the error.
画像をクリックして展開