Microsoft Architect Forum、Enterprise Windows 8 開発セッションフォローアップ Part 2
皆様、こんにちは!
Part 1~Part 3ではWindows ストアアプリとWindows Azure連携デモにつき書きます。Part 1では主に今回のデモで使用された技術の基本的な使い方、Part 2では実際の実装コード(店舗管理者用ストアアプ側)、Part 3では一般ユーザー用ストアアプリ側(Mobile Services)、そしてPart 4で、Office 365およびSharePointとの連携のコード、について書く予定です。
今回は、Part 2について解説します。
1.セッションスライドについて
こちらに公開してあります。
MAF2013 Enterprise Windows 8 – Architecture for rapid development of WinRT apps from Shotaro Suzuki
2.デモについて
Part1 ~ Part 3 では、Windows Azure連携シナリオである、 ”オンライン楽器ストアプリ”について、ご紹介します。このようなアプリでした。
アーキテクチャー概要はこちらです。
店舗管理者用のアプリは、Entity Framework/CodeFirst でデータベースを構成し、SQL Database に商品詳細情報(文字・数値データ)、Windows Azure ストレージ(BLOB)に画像を登録します。そして、ASP.NET Web API で REST サービスを作成・公開し、Windows Azure Websites で当該サービスをホストし、Windows ストアアプリから商品情報を登録・編集して、商品登録と管理を行います。
一般ユーザー用は、店舗管理者用のView を若干変更し、同じデータソースの読み取りに加えて、購入手続き向けに、Mobile Servicesも使って認証やPush通知を行う仕組みになっています。
すなわち、下記の要素からなります。
(1) Windows Azure Web サイト
(2) Windows Azure SQL データベース
(3) ASP.NET Web API
(4) ADO.NET Entity Framework
(5) Windows Azure ストレージサービス
(6) Windows 8 ストアアプリ との連携
(7) Windows Azure Mobile Services との連携
Part2 では、**今回のデモの実装内容として、(4)~(6)まで**を順に見ていきましょう。
(4) ADO.NET Entity Framework
データアクセスコンポーネントの実装
ここでは、PhotoMgr.WebApiのCRUD操作のメソッドに、クライアントからアップロードされたデータのうち、画像のタイトルやコメントなど画像データ以外をADO.NET Entity Framework によりデータベースから参照およびデータベースへ書き込む機能を実装します。
ステップ1. エンティティクラスの作成
1. Visual Studio 2012でPhotoMgr.WebApiプロジェクトを開いていない場合は開きます。
2. ソリューションエクスプローラーでPhotoMgr.WebApiプロジェクトの「Models」フォルダーを右クリックし、[追加]-[クラス]をクリックします。
3. [新しい項目の追加]ダイアログのテンプレートの一覧で [クラス] が選択されていることを確認し、[名前]欄に「Item」と入力後[追加]ボタンをクリックします。
4. 生成されたItem.csファイル内のItemクラスに下記のコードを貼り付けます。Itemクラスは以下のようになります。
public class Item
{
public string ItemId { get; set; }
public string Title { get; set; }
public string Subtitle { get; set; }
public string ImagePath { get; set; }
public string FileName { get; set; }
public string ContentType { get; set; }
public string Description { get; set; }
public byte[] ImageBytes { get; set; }
}
5. [ビルド]メニュー内の[PhotoMgr.WebApiのビルド]をクリックします。ビルドエラーが発生した場合はエラーを修正します。
ステップ2. コントローラー/コンテキストの作成
1. ソリューションエクスプローラーでPhotoMgr.WebApiプロジェクトの「Controllers」フォルダーを右クリックし、[追加]-[コントローラー]をクリックします。
2. [コントローラーの追加]ダイアログで、[コントローラー]欄に「ItemsController」と入力、[テンプレート]欄は「Entity Framework を使用した、読み取り/書き込み操作のあるAPIコントローラー」を選択、[モデルクラス]欄は「Item (PhotoMgr.WebApi.Models)」を選択します。
3. [データコンテキスト クラス]欄のリストで、「新しいデータコンテキスト」を選択します。[新しいデータコンテキスト]ダイアログが表示されるので、[新しいデータコンテキスト型]欄に「PhotoMgr.WebApi.Models.PhotoContext」と指定し[OK]ボタンをクリックします。
4. [コントローラーの追加]ダイアログで、[追加]ボタンをクリックします。
5. 「ItemsController.cs」ファイルが生成され、ItemsControllerクラス内にGetItemsメソッド、GetItemメソッド、PutItemメソッド、PostItemメソッド、DeleteItemメソッドが含まれていることを確認します。
6. [ビルド]メニュー内の[PhotoMgr.WebApiのビルド]をクリックします。ビルドエラーが発生した場合はエラーを修正します。
ステップ3. コードファーストによるデータベースの作成
1. [ツール]メニュー内の[ライブラリパッケージマネージャー]- [パッケージマネージャーコンソール]をクリックします。画面下部に[パッケージマネージャーコンソール]が表示されます。
2. 以下の1行を、[パッケージマネージャーコンソール]のコマンドプロンプトに打ち込んでEnterキーを押下します。
enable-migrations -ContextTypeName PhotoMgr.WebApi.Models.PhotoContext
3. 以下の1行を、[パッケージマネージャーコンソール]のコマンドプロンプトに打ち込み、Enterキーを押下します。
add-migration Initial
4. ソリューションエクスプローラーでPhotoMgr.WebApiプロジェクトの「Migrations」フォルダー内に「Configuration.cs」ファイルが生成されていることを確認します。
5. 「Configuration.cs」ファイル内の4つのusingディレクティブの次の行に、以下のコードを追加します。
using PhotoMgr.WebApi.Models;
6. 「Configuration.cs」ファイル内のSeedメソッドの末尾に、下記の内容を貼り付けます。
context.Items.AddOrUpdate(
p => p.ItemId,
new Item { ItemId = "11111111-1111-1111-1111-111111111111", Title = "Microsoft Logo", Subtitle = "mslogo", ImagePath = "https://i.s-microsoft.com/global/ImageStore/PublishingImages/logos/hp/logo-lg-2x.png", FileName = "logo-lg-2x.png", ContentType = "image/png", Description = "mslogo", ImageBytes = null },
new Item { ItemId = "22222222-2222-2222-2222-222222222222", Title = "msn Logo", Subtitle = "msnLogo", ImagePath = "https://kaw.stb00.s-msn.com/i/67/64747463334927CC5F5C94132FFA5.gif", FileName = "msnlogo.gif", ContentType = "image/gif", Description = "msnlogo", ImageBytes = null }
);
7. [ビルド]メニュー内の[PhotoMgr.WebApiのビルド]をクリックします。ビルドエラーが発生した場合はエラーを修正します。
8. 以下の1行をコピーして、[パッケージマネージャーコンソール]のコマンドプロンプトに貼り付けてEnterキーを押下します。
update-database
ステップ4. 実行結果の確認
1. Visual Studio 2012のメニューで、[デバッグ]-[デバッグ開始]をクリックします。プロジェクトがビルドされ、実行結果がブラウザー上に表示されます。
2. ブラウザーのアドレス欄の末尾に api/Items/ と追加します(元のアドレスが「https://localhost:XXXXX/」であった場合は、 https://localhost:XXXXX/api/Items/ になります)。追加後、Enterキーを押下します
3. ブラウザーに以下の画面が表示されます。
4. [ファイルを開く] ボタンをクリックします。ASP.NET Web APIから返されたレスポンスが表示されます。内容を確認後、Items.jsonファイルを閉じます(保存は不要です)。
5. Visual Studio 2012のメニューから、[デバッグ]-[デバッグの停止]をクリックします
(5) Windows Azure ストレージサービス
1. ストレージサービスの利用
続いて、PhotoMgr.WebApiのCRUD操作のメソッドに、クライアントからアップロードされたデータのうち、画像データをWindows Azure ストレージサービスのBlobにアップロードする機能を実装します。
ステップ1. ストレージアカウントの作成
1. ブラウザーで、Windows Azure 管理ポータル(https://manage.windowsazure.com/) にアクセスします。
2. Windows Azure 管理ポータルの下部にある、[新規]ボタンをクリックします。
3. 展開されたメニューから、[データサービス]をクリックし、サブメニューにある[ストレージ]をクリックします。さらにサブメニューが表示されるので、[簡易作成]をクリックします。
4. [データベースの詳細設定]画面で、以下の各項目を指定し、✓をクリックします
URL |
任意の文字列(core.windows.netドメイン下で一意な文字列) |
地域 / アフィニティグループ |
東アジア |
5. しばらくすると、ストレージアカウントの作成が完了し、管理ポータル上に作成したストレージの情報が表示されます。
ステップ2. ストレージの利用設定
1. Visual Studioのソリューションエクスプローラーで、PhotoMgr.WebApi プロジェクトのルートにある「Web.config」ファイルをダブルクリックして開きます。
2. <configuration>要素の子要素として、<appSettings>要素が存在することを確認します。
3. <appSettings>要素の子要素として、以下の3つの要素を追加します。
<add key="storageAccountName" value=""/>
<add key="storageManageKey" value=""/>
<add key="blobContainerName" value=""/>
4. Windows Azure 管理ポータルで、ステップ1で作成したストレージアカウント名をクリックします。作成したストレージのダッシュボードが表示されます。
5. ストレージのダッシュボードで、画面下部にある[キーの管理]ボタンをクリックします。
6. [アクセスキーの管理]ダイアログが表示されるので、[ストレージアカウント名]欄の右側にあるコピーボタンをクリックします。ストレージアカウント名がクリップボードにコピーされます(クリップボードへのアクセス許可が求められた場合は、アクセスを許可してください)。
7. コピーしたストレージアカウント名を、PhotoMgr.WebApi プロジェクトの「Web.config」ファイルにあるkey属性が” storageAccountName”のadd要素に、value属性として貼り付けます。
<add key="storageAccountName" value="ストレージアカウント名"/>
<add key="storageManageKey" value=""/>
<add key="blobContainerName" value=""/>
8. [アクセスキーの管理]ダイアログで、[プライマリアクセスキー]欄の右側にあるコピーボタンをクリックします。アクセスキーがクリップボードにコピーされます。
9. コピーしたアクセスキーを、PhotoMgr.WebApi プロジェクトの「Web.config」ファイルにあるkey属性が” storageManageKey”のadd要素に、value属性として貼り付けます。
<add key="storageAccountName" value="ストレージアカウント名"/>
<add key="storageManageKey" value="アクセスキー"/>
<add key="blobContainerName" value=""/>
10. PhotoMgr.WebApi プロジェクトの「Web.config」ファイルにあるkey属性が” blobContainerName”のadd要素に、value属性として「pictures」と入力します。
<add key="storageAccountName" value="ストレージアカウント名"/>
<add key="storageManageKey" value="アクセスキー"/>
<add key="blobContainerName" value="pictures"/>
3. Blobにアクセスするコードの実装
1. Visual Studio 2012のソリューションエクスプローラーで、PhotoMgr.WebApi プロジェクトを右クリックし、[参照の追加]を選択します。
2. [参照マネージャー]ダイアログが表示されるので、右上の検索ボックスに「azure」と入力します。
3. 表示されたアセンブリの一覧から、「Microsoft.WindowsAzure.StorageClient」アセンブリのチェックボックスを有効にし、[OK]ボタンをクリックします。
4. Visual Studio 2012のソリューションエクスプローラーで、PhotoMgr.WebApi プロジェクトのControllersフォルダー内にあるItemsController.csをダブルクリックして開きます。
5. ItemsController.csの上部にある一連のusingディレクティブの末尾に、以下のusingディレクティブを追加します。
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
6. ItemsControllerクラスのPutItemメソッド内のコードを削除します。
7. 下記の内容を、ItemsControllerクラスのPutItemメソッド内に貼り付けます。
Item itemFromDB = db.Items.Find(id);
if (item == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
if (!string.IsNullOrEmpty(item.Title))
{
itemFromDB.Title = item.Title;
}
if (!string.IsNullOrEmpty(item.Description))
{
itemFromDB.Description = item.Description;
}
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
return Request.CreateResponse(HttpStatusCode.OK);
8. 下記の内容を、ItemsControllerクラスのPostItemメソッド内の先頭に貼り付けます。
string storageAccountName = System.Web.Configuration.WebConfigurationManager.AppSettings["storageAccountName"];
string storageManageKey = System.Web.Configuration.WebConfigurationManager.AppSettings["storageManageKey"];
string blobContainerName = System.Web.Configuration.WebConfigurationManager.AppSettings["blobContainerName"];
CloudStorageAccount account;
CloudBlobClient blobClient;
CloudBlobContainer container;
CloudBlob blob;
try
{
account = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName="
+ storageAccountName
+ ";AccountKey=" + storageManageKey);
blobClient = account.CreateCloudBlobClient();
blobClient.RetryPolicy = RetryPolicies.Retry(4, TimeSpan.FromMilliseconds(500));
container = blobClient.GetContainerReference(blobContainerName);
container.CreateIfNotExist();
BlobContainerPermissions permissions = container.GetPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permissions);
string blobName = string.Format("{0}_{1}", item.ItemId, item.FileName);
blob = container.GetBlobReference(blobName);
byte[] imageByteArray = (byte[])item.ImageBytes;
System.IO.MemoryStream ms = new System.IO.MemoryStream(imageByteArray);
blob.UploadFromStream(ms);
blob.Properties.ContentType = item.ContentType;
blob.SetProperties();
item.ImageBytes = null;
}
catch (StorageException)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
item.ImagePath = blob.Uri.ToString();
9. 下記の内容を、ItemsControllerクラスのDeleteItemメソッド内の先頭に貼り付けます。
string storageAccountName = System.Web.Configuration.WebConfigurationManager.AppSettings["storageAccountName"];
string storageManageKey = System.Web.Configuration.WebConfigurationManager.AppSettings["storageManageKey"];
string blobContainerName = System.Web.Configuration.WebConfigurationManager.AppSettings["blobContainerName"];
CloudStorageAccount account;
CloudBlobClient blobClient;
CloudBlob blob;
10. 下記の内容を、ItemsControllerクラスのDeleteItemメソッド内にあるtryブロックの末尾(db.SaveChanges( ); の後)に貼り付けます。
account = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName="
+ storageAccountName
+ ";AccountKey=" + storageManageKey);
blobClient = account.CreateCloudBlobClient();
blobClient.RetryPolicy = RetryPolicies.Retry(4, TimeSpan.FromMilliseconds(500));
blob = blobClient.GetBlobReference(item.ImagePath);
blob.Delete();
11. 下記の内容を、ItemsControllerクラスのDeleteItemメソッド内にあるcatchブロックの下に貼り付けます。
catch (StorageClientException)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
ここまでの手順で、DeleteItemメソッドは以下のようになります。
ステップ4. 実行結果の確認
1. Visual Studio 2012のメニューで、[デバッグ]-[デバッグ開始]をクリックします。プロジェクトがビルドされ、実行結果がブラウザー上に表示されます。
2. ブラウザーのアドレス欄の末尾に「api/Items/」と追加します(元のアドレスが https://localhost:XXXXX 」であった場合は、https://localhost:XXXXX/api/Items/ になります)。追加後、Enterキーを押下します
3. ブラウザーに以下の画面が表示されます。
4. [ファイルを開く] ボタンをクリックします。ASP.NET Web APIから返されたレスポンスが表示されます。内容を確認後、Items.jsonファイルを閉じます(保存は不要です)。
5. Visual Studio 2012のメニューから、[デバッグ]-[デバッグの停止]をクリックします
ステップ5. Windows Azure Web サイト への配置
1. Visual Studio 2012のソリューションエクスプローラーで、プロジェクト名(PhotoMgr.WebApi)を右クリックし、[発行]を選択します。
2. [Webの発行]ダイアログで、[インポート]ボタンをクリックします。
3. [発行の設定のインポート]ダイアログで、Windows Azure Webサイトの演習でデスクトップに保存したプロファイルのファイル(Webサイト名.azurewebsites.net.PublishSettings)を開きます。
4. [Webの発行]ダイアログに戻るので、[次へ]ボタンをクリックします。
5. 発行の設定画面で、[ファイル発行オプション]ノードを展開し、[発行先で追加のファイルを削除]のチェックボックスを有効にします。次に、[PhotoContext]ノードの[Code First Migrationsを実行する(アプリケーションの起動時に実行する)]のチェックボックスを有効にします。最後に[発行]ボタンをクリックします。
6. Visual Studio 2012からの発行が終了すると、作成したWebアプリケーションがブラウザーで表示されます。ブラウザーのアドレス欄を確認し、アドレスが https://********.azurewebsites.net/(********は作成したWebサイトの名前)になっていることを確認します。
7. ブラウザーのアドレス欄の末尾に「api/Items/」と追加します。追加後、Enterキーを押下します
8. ブラウザーに以下の画面が表示されます。
9. [ファイルを開く] ボタンをクリックします。ASP.NET Web APIから返されたレスポンスが表示されます。内容を確認後、Items.jsonファイルを閉じます(保存は不要です)。
(6) Windows ストア アプリとの連携
演習1. Windows ストア アプリ プロジェクトの作成
Web API を利用する Windows ストア アプリのプロジェクトを作成します。この時、Web で公開されている Basic Photo プロジェクトテンプレートを利用します。
ステップ1. BasicPhoto テンプレートのダウンロード
1. Internet Explorer を利用し、以下の Web サイトにアクセスします。
https://msdn.microsoft.com/ja-jp/jj556277.aspx
2. 「Windows 8 アプリ開発体験テンプレート」ページが表示されることを確認します。
3. 「Basic Photo テンプレート XAML/C# 用 Version 1.0」の「RTM 版ダウンロードリンク」ボタンをクリックします。
4. 以下のポップアップが表示されたら、[名前を付けて保存] を選択します。
5. [名前を付けて保存] ダイアログで、以下のフォルダーを指定し、テンプレートファイルを保存します。
ドキュメント\Visual Studio 2012\Templates\ProjectTemplates\Visual C#
ステップ2. イメージファイルのダウンロード
ここは私のデモは、軽音楽部のメンバーから送って戴いた楽器の画像数十枚を入れたのですが、これは説明部分の記述が入れやすかったからにほかなりませんw もしそういった適当な素材がない場合には、下記のようにアートギャラリーの画像を利用する手もあります。
1. Internet Explorer で、同じページ内の「Windows アプリ アートギャラリー」リンクをクリックします。
2. 「Windows アプリ アートギャラリー」ページで、「自然(写真)」カテゴリーの中から、好みのカテゴリーを選択します。
3. 以下のポップアップが表示されたら、[名前を付けて保存] を選択します。
4. [名前を付けて保存] ダイアログで、以下のフォルダーを指定し、テンプレートファイルを保存します。
ピクチャ
5. Windows エクスプローラーでピクチャフォルダーを開きます。
6. ダウンロードした zip ファイルを右クリックし、[すべて展開] を選択します。
7. [圧縮(ZIP 形式)フォルダーの展開] ダイアログで、[展開] ボタンをクリックします。
8. イメージファイルが展開されたことを確認します。
ステップ3. PhotoMgrClient プロジェクトの作成
1. Visual Studio 2012 を起動します。
2. [ファイル]-[新規作成]-[プロジェクト] を選択します。
3. [新しいプロジェクト] ダイアログで、以下の設定をしたら、[OK] ボタンをクリックします。
テンプレート |
[Visual C#]-[Basic Photo for Win8] |
名前 |
PhotoMgrClient |
演習2. Web API を利用したデータの取得
Windows ストア アプリから Web API の Get メソッドを呼び出し、イメージ一覧を取得し、GridView コントロールに表示します。
ステップ1. データソースファイルの追加
1. Skydrive上にある以下の公開フォルダーを開き中にあるZIPファイルを、適当なフォルダに展開してください。
MAFセッションWindows ストアアプリ用ファイル群.zip
2. フォルダー内のすべてのフォルダーとファイルを選択し、コピーします。
3. Visual Studio に戻り、「PhotoMgrClient」プロジェクトを右クリックしたら、[貼り付け] を選択します。
4. [フォルダーの結合] ダイアログが表示されたら、[OK] ボタンをクリックします。
5. [ターゲットファイルが存在します] ダイアログが表示されたら、[はい] ボタンをクリックします。
6. [ソリューションエクスプローラー] で、以下のファイルを開きます。
DataModel\PhotoDataSource.cs
7. PhotoDataSource.cs ファイルに、PhotoDataSource、PhotoDataCommon、PhotoDataGroup 、PhotoDataItem クラスが定義されていることを確認します。
ポイント!
これらのクラスは階層構造を形成しています。PhotoDataSource クラスは最上位のクラスで、その下に イメージのカテゴリー情報を格納する PhotoDataGroup グラスがあります。さらにその下には、各イメージファイルの情報を格納する PhotoDataItem クラスがあります。
8. PhotoDataSource クラスのコンストラクターを開き、以下の通り変更します。
※変更すべき個所は強調表示されています。
static PhotoDataSource()
{
var group = new PhotoDataGroup(
Guid.NewGuid().ToString(),
"旅行",
"旅行",
"Assets/DarkGray.png",
"Group Description: ..(途中省略).. ante a ante");
PhotoDataSource.AllGroups.Add(group);
}
ポイント!
ここでは写真のカテゴリーを作成しています。Basic Photo テンプレートでは、写真のカテゴリーをアプリの中から新規に作成できますが、ここでは「旅行」というカテゴリーを1つだけ作成し、その中に画像を登録します。
ステップ2. 接続先のAzure サイトの設定
1. [ソリューションエクスプローラー] で、App.xaml ファイルを開きます。
2. 以下のコメントを探します。
<!--アプリケーション固有のリソース -->
3. 探したコメントの後に、以下のコードを追加します。
<x:String x:Key="ServiceUrl">
https://AzureのWebサイト名/api/Items
</x:String>
※「AzureのWebサイト名」 には、以前の項目でWindows Azure 上に作成した Web API サイトのURL を指定します。
参考
これにより、C#のコードの中から、Azure サイトのURL を「ServiceUrl」という名前で呼び出すことができるようになります。
ステップ3. PhotoDataSource の呼び出し
1. [ソリューションエクスプローラー] で、App.xaml.cs ファイルを開きます。
2. 以下のコードを追加します。
using PhotoMgrClient.Data;
3. App.xaml.cs ファイルの「OnLaunched」メソッドで以下のコードを探します。
// フレームを現在のウィンドウに配置します
Window.Current.Content = rootFrame;
ポイント !
OnLaunched メソッドは、アプリが起動した際に呼び出されるメソッドです。
4. 探したコードの下に、以下のコードを追加します。
await PhotoDataSource.LoadRemoteDataAsync();
5. 入力したコードのうち、 .LoadRemoteDataAsync の部分を選択したら、右クリックします。
6. [定義へ移動] を選択します。
7. PhotoDatasource クラスの LoadRemoteDataAsync メソッドに移動したら、以下のコードをメソッドの最初に追加します。
var client = new HttpClient();
object serviceUrl;
App.Current.Resources.TryGetValue("ServiceUrl", out serviceUrl);
var response = await client.GetAsync(serviceUrl as string);
var result = await response.Content.ReadAsStringAsync();
var pictures = JsonArray.Parse(result);
参考
「App.Current.Resources.TryGetValue("ServiceUrl", out serviceUrl); 」により、ステップ2で App.xaml ファイルに定義した Azure のWeb サイトの URL が取得可能となります。
ポイント!
HttpClient クラスのGetAsync メソッドにより、HTTP GET のリクエストを発行することができます。このメソッドは非同期で呼び出されるため、await キーワードが必要となります。
また、このコードではHTTP レスポンスは戻り値の result 変数に格納されます。この戻り値はJSON形式であるため、Parse メソッドによりパースしています。
ステップ4. グループ詳細ページの変更
1. [ソリューションエクスプローラー] で、GroupDetailPage.xaml.cs ファイルを開きます。
2. 以下のコードをファイルの先頭に追加します。
var client = new HttpClient();
object serviceUrl;
App.Current.Resources.TryGetValue("ServiceUrl", out serviceUrl);
var response = await client.GetAsync(serviceUrl as string);
var result = await response.Content.ReadAsStringAsync();
var pictures = JsonArray.Parse(result);
3. GroupDetailPage クラスに、以下のプライベート変数を追加します。
private PhotoDataGroup group;
4. LoadState メソッドで、以下のコードを探します。
var group = SampleDataSource.GetGroup((String)navigationParameter);
5. 探したコードを以下の通り変更します。
※var を削除し、Sample を Photo に書き換えます
var group = PhotoDataSource.GetGroup((String)navigationParameter);
group = PhotoDataSource.GetGroup((String)navigationParameter);
ポイント!
Basic Photo プロジェクトテンプレートでは、SampleDataSource クラスで定義されているデータを表示しています。ステップ4~ステップ6の操作では、これを PhotoDataSource クラスを表示するようにコードを書き換えていきます。
6. ItemView_ItemClick イベントハンドラーで、以下のコードを探します。
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
7. 探したコードを以下の通り変更します。
var itemId = ((PhotoDataItem)e.ClickedItem).ItemId;
ステップ5. スタートページの変更
1. [ソリューションエクスプローラー] で、GroupeItemsPage.xaml.cs ファイルを開きます。
2. LoadState メソッドで、以下のコードを探します。
var sampleDataGroups =
SampleDataSource.GetGroups((String)navigationParameter);
3. 探したコードを以下の通り変更します。
var sampleDataGroups =
PhotoDataSource.GetGroups((String)navigationParameter);
4. Header_Click イベントハンドラーで、以下のコードを探します。
this.Frame.Navigate(typeof(GroupDetailPage),
((SampleDataGroup)group).UniqueId);
5. 探したコードを以下の通り変更します。
this.Frame.Navigate(typeof(GroupDetailPage),
((PhotoDataGroup)group).GroupId);
6. ItemView_ItemClick イベントハンドラーで、以下のコードを探します。
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
7. 探したコードを以下の通り変更します。
var itemId = ((PhotoDataItem)e.ClickedItem).ItemId;
8. Group_Tapped イベントハンドラーで、以下のコードを探します。
this.Frame.Navigate(typeof(GroupDetailPage),
((SampleDataGroup)group).UniqueId);
9. 探したコードを以下の通り変更します。
this.Frame.Navigate(typeof(GroupDetailPage),
((PhotoDataGroup)group).GroupId);
ステップ6. 項目詳細ページを変更
1. [ソリューションエクスプローラー] で、ItemDetailPage.xaml.cs ファイルを開きます。
2. 以下のコードを先頭に追加します。
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Text;
3. ItemDetailPage クラスに、以下のプライベート変数を追加します。
private PhotoDataGroup group;
4. LoadState メソッドで、以下のコードを探します。
var item = SampleDataSource.GetItem((String)navigationParameter);
5. 探したコードを以下の通り変更します。
var item = PhotoDataSource.GetItem((String)navigationParameter);
6. SaveState メソッドで、以下のコードを探します。
var selectedItem = (SampleDataItem)this.flipView.SelectedItem;
pageState["SelectedItem"] = selectedItem.UniqueId;
7. 探したコードを以下の通り変更します。
var selectedItem = (PhotoDataItem)this.flipView.SelectedItem;
pageState["SelectedItem"] = selectedItem.ItemId;
8. OnDataRequested メソッドで、以下のコードを探します。
var item = this.flipView.SelectedItem as Data.SampleDataItem;
9. 探したコードを以下の通り変更します。
var item = this.flipView.SelectedItem as Data.PhotoDataItem;
10. ItemDetailPage.xaml ファイルを開きます。
11. [ソリューションエクスプローラー] で、ItemDetailPage.xaml ファイルを開きます。
12. 以下の要素を探します。※80行目周辺にあります
<Paragraph>
<Run FontWeight="SemiLight" Text="{Binding Content}"/>
</Paragraph>
13. 探した要素を以下の通り変更します。
<Paragraph>
<Run FontWeight="SemiLight" Text="{Binding Description}"/>
</Paragraph>
ステップ7. 動作確認
1. アプリを実行します。
2. 「旅行」カテゴリーが作成されていること、また、Web API 作成時に Seed メソッドで登録したイメージが表示されることを確認します。
3. Visual Studio に戻り、アプリを終了します。
参考
画面左上端をポイントすると、アプリを切り替えることができます。
演習3. Web API を利用したデータの追加
Windows ストア アプリから Web API の Post メソッドを呼び出し、イメージ一覧にイメージを追加します。
ステップ1. アプリバー用スタイルの追加
1. [ソリューションエクスプローラー] で、Common\StandardStyles.xaml ファイルを開きます。
2. 以下のコメントタグを探します。
※404行目あたりにあります
<!--
標準 AppBarButton スタイルは、Button と ToggleButton で使用されます。
AppBarButton スタイルは、Segoe UI Symbol フォントのグリフごとに用意されます。
参照するスタイルのコメントを解除します (すべてのスタイルが必要なわけではありません)。
-->
3. 探したタグの下に、以下のタグを追加します。
<Style x:Key="AddPhotoAppBarButtonStyle"
TargetType="ButtonBase"
BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId"
Value="AddAppBarButton"/>
<Setter Property="AutomationProperties.Name"
Value="写真追加"/>
<Setter Property="Content"
Value=""/>
</Style>
<Style x:Key="DeleteAppBarButtonStyle"
TargetType="ButtonBase"
BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId"
Value="DeleteAppBarButton"/>
<Setter Property="AutomationProperties.Name"
Value="削除"/>
<Setter Property="Content"
Value=""/>
</Style>
<Style x:Key="EditNameAppBarButtonStyle"
TargetType="ButtonBase"
BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId"
Value="EditAppBarButton"/>
<Setter Property="AutomationProperties.Name"
Value="タイトル編集"/>
<Setter Property="Content"
Value=""/>
</Style>
<Style x:Key="EditCommentAppBarButtonStyle"
TargetType="ButtonBase"
BasedOn="{StaticResource AppBarButtonStyle}">
<Setter Property="AutomationProperties.AutomationId"
Value="EditAppBarButton"/>
<Setter Property="AutomationProperties.Name"
Value="コメント編集"/>
<Setter Property="Content"
Value=""/>
</Style>
参考
ここで指定したスタイルは、この後の手順で追加するアプリバー上のコマンドで使用します。
ステップ2. アプリバーの追加
1. [ソリューションエクスプローラー] で、GroupDetailPage.xaml ファイルを開きます。
2. 一番最後のタグ( </common:LayoutAwarePage>) の前に、以下のタグを追加します。
<Page.BottomAppBar>
<AppBar>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal"/>
<StackPanel Grid.Column="1"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button x:Name="buttonAddPhoto"
Style=
"{StaticResource AddPhotoAppBarButtonStyle}" />
</StackPanel>
</Grid>
</AppBar>
</Page.BottomAppBar>
参考
< AppBar > は、アプリバーを定義する UI 要素です。この中に、<Button> UI要素を挿入して、コマンドボタンを定義しています。
ステップ3. イメージを追加するコードの追加
1. 貼り付けたタグの中から、以下のタグをクリックします。
<Button x:Name="buttonAddPhoto"
Style= "{StaticResource AddPhotoAppBarButtonStyle}" />
2. デザイン画面に、「写真追加」ボタンが表示されたら、ダブルクリックします。
3. buttonAddPhoto_Click イベントハンドラーに、以下のコードを追加します。
private async void buttonAddPhoto_Click(object sender,
RoutedEventArgs e)
{
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".png");
picker.ViewMode = PickerViewMode.Thumbnail;
var photoFile = await picker.PickSingleFileAsync();
if (photoFile != null)
{
var newItem = new PhotoDataItem();
newItem.ItemId = Guid.NewGuid().ToString();
newItem.Title = photoFile.Name;
newItem.Subtitle = photoFile.Name;
newItem.ImagePath = photoFile.Path;
newItem.Description = "";
newItem.ContentType = photoFile.ContentType;
newItem.FileName = photoFile.Name;
newItem.Group = group;
group.Items.Add(newItem);
await newItem.SetImageAsync(photoFile);
await CreateItem(newItem);
}
}
4. 同じファイルに、以下のメソッドを追加します。
private async Task CreateItem(PhotoDataItem item)
{
object serviceUrl;
App.Current.Resources.TryGetValue("ServiceUrl"
, out serviceUrl);
using (HttpClient client = new HttpClient())
{
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(typeof(PhotoDataItem));
using (MemoryStream stream = new MemoryStream())
{
serializer.WriteObject(stream, item);
stream.Seek(0, SeekOrigin.Begin);
var json = new StreamReader(stream).ReadToEnd();
var response = await client.PostAsync(
serviceUrl as string, new StringContent
(json, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
}
}
ポイント !
HttpClient クラスのPostAsyncメソッドにより、HTTP POST のリクエストを発行することができます。このメソッドは非同期で呼び出されるため、await キーワードが必要です。
また、POST するデータは、DataContractJsonSerializer クラスを利用して JSON 形式にシリアライズしてから送信しています。
ステップ4. 動作確認
1. アプリを実行します。
2. 「旅行」カテゴリーをクリックします。
3. 画面上で右クリックし、アプリバーから「写真追加」コマンドをクリックします。
4. ファイルピッカーが表示されたら、左上の「ファイル」をクリックします。
5. [ピクチャ] を選択すると、ピクチャフォルダー内のフォルダーとファイルが表示されます。
6. 演習1でダウンロードしたファイルを選択し、[開く] ボタンをクリックします。
7. [旅行] カテゴリー内にイメージが追加されます。
8. 手順3~手順6を繰り返し、いくつかイメージを追加します。
9. Visual Studio に戻り、アプリを終了します。
10. 再度アプリを実行し、イメージが Web API 経由で登録されたことを確認します。
11. Visual Studio に戻り、アプリを終了します。
演習4:Web API を利用したデータの削除
Windows ストア アプリから Web API の Delete メソッドを呼び出し、イメージ一覧からイメージを削除します。
ステップ1. アプリバーの追加
1. [ソリューションエクスプローラー] で、ItemDetailPage..xaml ファイルで、以下のタグを削除します。
参考
ページの下のほうにあります。
<Page.BottomAppBar>
<!-- [Customize Point]必要な機能を追加してください -->
<AppBar>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal"/>
<StackPanel Grid.Column="1"
HorizontalAlignment="Right"
Orientation="Horizontal"/>
</Grid>
</AppBar>
</Page.BottomAppBar>
2. 削除したタグの代わりに、以下のタグを追加します。
<Page.BottomAppBar>
<AppBar>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<Button x:Name="buttonRemove"
Style=
"{StaticResource DeleteAppBarButtonStyle}" />
</StackPanel>
<StackPanel Grid.Column="1"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Style=
"{StaticResource EditNameAppBarButtonStyle}"
x:Name="buttonEditTitle" />
<Button Style=
"{StaticResource EditCommentAppBarButtonStyle}"
x:Name="buttonEditComment" />
</StackPanel>
</Grid>
</AppBar>
</Page.BottomAppBar>
参考
ここでは、アプリバー上に3つのコマンド(削除/タイトル編集/コメント編集)を作成しています。
ステップ2. イメージを削除するコードの追加
1. 貼り付けたタグのうち、以下のタグをクリックします。
<Button x:Name="buttonRemove"
Style="{StaticResource DeleteAppBarButtonStyle}" />
2. デザイン画面に、「削除」ボタンが表示されたら、ダブルクリックし、buttonRemove_Click イベントハンドラーを表示します。
3. buttonRemove_Clickイベントハンドラーに、以下のコードを追加します。
var item = this.flipView.SelectedItem as Data.PhotoDataItem;
DeleteItem(item);
item.Group.Items.Remove(item);
item.Group.TopItems.Remove(item);
if (this.Frame != null && this.Frame.CanGoBack)
{
this.Frame.GoBack();
}
4. 同じファイルに、以下のメソッドを追加します。
private async void DeleteItem(PhotoDataItem item)
{
object serviceUrl;
App.Current.Resources.TryGetValue("ServiceUrl", out serviceUrl);
using (HttpClient client = new HttpClient())
{
var response = await client.DeleteAsync
(serviceUrl as string + "/" + item.ItemId);
response.EnsureSuccessStatusCode();
}
}
ポイント !
HttpClient クラスのDeleteAsyncメソッドにより、HTTP DELETE のリクエストを発行することができます。このメソッドは非同期で呼び出されるため、await キーワードが必要です。
削除対象のデータは、URLの最後に、ItemId を渡すことで特定します。
ステップ3. 動作確認
1. アプリを実行します。
2. 「旅行」カテゴリーをクリックします。
3. イメージをクリックします。
4. イメージの詳細ページで画面上を右クリックし、アプリバーから「削除」コマンドを選択し、イメージが削除されることを選択します。
5. Visual Studio に戻り、アプリを終了します。
演習5:Web API を利用したデータの変更
Windows ストア アプリから Web API の Put メソッドを呼び出し、イメージ一覧のイメージを変更します。ここでは、Item の Title と Description を変更します。
ステップ1. タイトルを変更するコードの追加
1. [ソリューションエクスプローラー] で、ItemDetailPage..xaml ファイルを開きます。
2. アプリバー内の「タイトル編集」コマンドを定義する以下のタグをクリックします。
<Button Style="{StaticResource EditNameAppBarButtonStyle}"
x:Name="buttonEditTitle" />
3. デザイン画面に、「タイトル編集」ボタンが表示されたら、ダブルクリックします。
4. buttonEditTitle_Click イベントハンドラーが表示されることを確認します。
5. ItemDetailPage クラスに、以下のプライベート変数を追加します。
private Popup popupEditTitle = null;
6. buttonEditTitle_Clickイベントハンドラーに、以下のコードを追加します。
if (popupEditTitle == null)
{
popupEditTitle = new Popup();
popupEditTitle.Closed += popupEditTitleFlyout_Closed;
}
EditTitleFlyout flyout = new EditTitleFlyout();
flyout.TargetItem = PhotoDataSource
.GetItem(((PhotoDataItem)this.flipView.SelectedItem).ItemId);
popupEditTitle.Child = flyout;
flyout.Transitions =
new Windows.UI.Xaml.Media.Animation.TransitionCollection();
var popupTransition =
new Windows.UI.Xaml.Media.Animation.PopupThemeTransition();
popupTransition.FromHorizontalOffset = -flyout.Width;
flyout.Transitions.Add(popupTransition);
popupEditTitle.IsOpen = true;
buttonEditTitle.IsEnabled = false;
参考
EditTitleFlyout クラスは EditTitleFlyout.xaml ファイルで定義されている Flyout パネルです。Flyout パネルは従来のWindows デスクトップアプリでいうダイアログボックスのような機能で、XAMLで表示する内容をデザインし、Popupクラス の IsOpen プロパティをtrue に設定することで表示します。この Popup クラスは、Flyout パネルが閉じられると、Closed イベントが発生します。そこで、ここではPoupuのClosedイベントに popupEditTitleFlyout_Closed を指定し、Flyout上で必要情報の入力が終わりパネルを閉じたときに呼ばれるイベントハンドラーを登録しています
7. 同じファイルに、以下の popupEditTitleFlyout_Closedイベントハンドラーを追加します。
private void popupEditTitleFlyout_Closed(object sender, object e)
{
var popup = sender as Popup;
var content = popup.Child as EditTitleFlyout;
if (content.IsSave)
{
PhotoDataItem updateItem = new PhotoDataItem();
var item = this.flipView.SelectedItem as Data.PhotoDataItem;
updateItem.ItemId = item.ItemId;
updateItem.Title = item.Title;
UpdateItem(updateItem);
}
buttonEditTitle.IsEnabled = true;
}
8. 同じファイルに、以下の UpdateItem メソッドを追加します。
private async void UpdateItem(PhotoDataItem item)
{
object serviceUrl;
App.Current.Resources.TryGetValue("ServiceUrl",
out serviceUrl);
using (HttpClient client = new HttpClient())
{
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(typeof(PhotoDataItem));
using (MemoryStream stream = new MemoryStream())
{
serializer.WriteObject(stream, item);
stream.Seek(0, SeekOrigin.Begin);
var json = new StreamReader(stream).ReadToEnd();
var response = await client.PutAsync
(serviceUrl as string + "/"
+ item.ItemId, new StringContent
(json, Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
}
}
}
ポイント !
HttpClient クラスのPutAsyncメソッドにより、HTTP PUT のリクエストを発行することができます。このメソッドは非同期で呼び出されるため、await キーワードが必要です。
また、更新対象となるデータは、URL の最後に ItemId を渡すことで特定します。そして更新するデータは、DataContractJsonSerializer クラスを利用して JSON 形式にシリアライズしてから送信しています。
ステップ2. 動作確認
1. アプリを実行します。
2. 「旅行」カテゴリーをクリックします。
3. イメージをクリックします。
4. イメージ詳細ページで画面上を右クリックし、アプリバーから「タイトル編集」コマンドを選択します。
5. 「タイトル入力」フライアウトで、タイトルを編集し、Enter キーを押すと、タイトルが変更できることを確認します。
6. Visual Studio に戻り、アプリを終了します。
ステップ3. コメントを変更するコードの追加
1. [ソリューションエクスプローラー] で、ItemDetailPage.xaml ファイルを開きます。
2. アプリバー内の「コメント編集」コマンドを定義する以下のタグをクリックします。
<Button Style="{StaticResource EditCommentAppBarButtonStyle}"
x:Name="buttonEditComment" />
3. デザイン画面に、「コメント編集」ボタンが表示されたら、ダブルクリックします。
4. buttonEditComment_Click イベントハンドラーが表示されることを確認します。
5. ItemDetailPage クラスに、以下のプライベート変数を追加します。
private Popup popupEditComment = null;
6. buttonEditComment_Clickイベントハンドラーに、以下のコードを追加します。
if (popupEditComment == null)
{
popupEditComment = new Popup();
popupEditComment.Closed += popupEditCommentFlyout_Closed;
}
EditCommentFlyout flyout = new EditCommentFlyout();
flyout.TargetItem = PhotoDataSource.GetItem
(((PhotoDataItem)this.flipView.SelectedItem).ItemId);
popupEditComment.Child = flyout;
flyout.Transitions =
new Windows.UI.Xaml.Media.Animation.TransitionCollection();
var popupTransition =
new Windows.UI.Xaml.Media.Animation.PopupThemeTransition();
popupTransition.FromHorizontalOffset = -flyout.Width;
flyout.Transitions.Add(popupTransition);
popupEditComment.IsOpen = true;
buttonEditComment.IsEnabled = false;
7. 同じファイルに、以下の popupEditCommentFlyout_Closedイベントハンドラーを追加します。
void popupEditCommentFlyout_Closed(object sender, object e)
{
var popup = sender as Popup;
var content = popup.Child as EditCommentFlyout;
if (content.IsSave)
{
PhotoDataItem updateItem = new PhotoDataItem();
var item = this.flipView.SelectedItem as Data.PhotoDataItem;
updateItem.ItemId = item.ItemId;
updateItem.Description = item.Description;
UpdateItem(updateItem);
}
buttonEditComment.IsEnabled = true;
}
ステップ4. 動作確認
1. アプリを実行します。
2. 「旅行」カテゴリーをクリックします。
3. イメージをクリックします。
4. イメージの詳細ページで画面上を右クリックし、アプリバーから「コメント編集」コマンドを選択します。
5. 「コメント入力」フライアウトで、コメントを編集し、[登録] ボタンを押すと、コメントが変更できることを確認します。
6. Visual Studio に戻り、アプリを終了します。
以上で、オンライン楽器ストアアプリの、管理者向けストアアプリ側の開発に関する解説は終わりです。いかがでしょうか?
Part 3では一般ユーザー向けのWindows ストアアプリ側を解説します。View の若干の変更と、(7)のMobile Servicesの利用の部分の実装になります。
その後、Part 4でO365とSharePoint連携のソースを解説します。
鈴木 章太郎
Comments
- Anonymous
February 05, 2014
記事拝読し、ストアアプリの構築を学習しております。 中に出てくる http://sdrv.ms/1080R2a ですが、skydriveのファイルは期限切れと表示されました。 再度アップしていただくことが可能であれば、ご検討お願い致します。