Just another WordPress.com site

月別アーカイブ: 12月 2012

MSDNで公開されているWindows8アプリ開発体験テンプレートは、手軽にストアアプリを開発するにはとても便利なものですが、RSSリーダーや画像閲覧アプリというような数個のテーマに限定されています。

そこで、Visual Studio 2012 に用意されている Gridテンプレートをカスタマイズして、ゼロの状態からアプリを作り始めたい場合もあります。

しかし、私の経験から、慣れないうちは、Gridテンプレートのどの部分にどのような修正を加えたら目的の機能を追加できるのか、わかりにくいのではないかと思います。

そこで、ここでは、Gridテンプレートのデータソースの変更の方法に焦点を絞って解説します。

ここで使用するGridテンプレートは、Visual BasicかC#で作成されたものを対象とします。
(ただし、コードを示すのはC#です。また、JavaScriptの場合も、似たようなアプリが作成されますが、プロジェクトの構造が異なるため、手順や修正する場所がやや異なります)

プロジェクトのデータソースは、DataModelフォルダのSampleDataSource.csファイル内にある 下記の 4つのクラスで提供されます。

  • SampleDataCommon
  • SampleDataGroup
  • SampleDataItem
  • SampleDataSource

このうち、SampleDataSourceクラスのデフォルトコンストラクタ(260行目あたり)を眺めると、プロジェクトで使用されているダミー・データがハードコーディングされていることがわかります。

従って、この部分を変更すれば、とりあえず、ダミー・データではなく、目的のデータ、あるいは少なくとも開発者にとっては意味のあるデータに変更できることになります。

しかし、やはり、いくら何でも、データをソースコード内にハードコーディングするのは、設計上も運用上もよくないと思われるので、ここでは、JSONデータを読み込む方式に変更してみたいと思います。

まず、その前に、自動生成されるデータソース(SampleDataSource)の構造を、UIとの比較で整理しておきましょう。

img01_columns

上の表を見ると、グループ用データも詳細用データもほぼ同じ構造をしており、詳細データのみ「コンテンツ(Content)」というカラムを持っていることがわかります。

ちなみに、それぞれのデータがGridテンプレートのUIのどの部分に表示されているかを図で示すと下記のようになります。

背景が水色の名称は「グループ用データ」のカラム名で、薄い黄色のものは「詳細用データ」のカラム名です。

まず、スタート画面です。

img02_start

次は、グループ詳細ページです。

img03_group

最後は、アイテム詳細ページです。

img04_item

上図を見てもわかるように、Gridテンプレートではグループ用画像と詳細データ用画像が使用されるので、本格的なアプリでは、それらをしっかりと用意しておくといいのですが、ここでは、サンプル画像をプロジェクト内のローカル・フォルダーに配置することにします。(公開するようなアプリでは、画像はローカルに配置するのではなく、インターネット上、例えば、Windows AzureのWebSiteなどに配置しておくといいでしょう)

なお、今回のサンプル画像は、Windows アプリ アート ギャラリーの春・夏・秋・冬の画像を使用しました。

今回は、プロジェクトにImagesフォルダを作成し、その配下に四季ごとにさらに4つのサブフォルダを作成し、それぞれの四季の画像を配置しました。(下図)

img05_images

次にJSONデータを用意します。
通常、JSONデータはWebから取得しますが、今回は、まずはテストのために、ローカルにJSONフォーマットのテキストファイルを配置し、テストに成功したら、Webのものを使用する、という手順にします。

VBやC#のプロジェクトの場合は、JSONデータをパースするときに、JSONに記述されている属性名を、SampleDataSourceのプロパティ名に対応させる処理が必要なので、必ずしも、JSONデータのの属性名を、上図で示したGridテンプレートで使用されるカラム名と同じ名前にする必要はありません。

例えば、下記のようなJSONデータを用意し、sample.txt などというファイル名で保存しておきます。

[ 
{ 
    "backgroundImage" : "images/spring/spring_01.jpg",
    "directions" : "(この部分は、アイテム詳細のコンテンツデータとなります) ",
    "group" : {
        "backgroundImage" : "images/spring/spring_group_01.png",
        "description" : "(この部分は、グループデータの説明文となります)",
        "key" : "Spring",
        "shortTitle" : "Spring",
        "title" : "Spring"
    },
    "key" : 1000,
    "shortTitle" : "春の風景1",
    "dummyPart" : "dummy dummy",
    "title" : "春の風景1"
},
{ 
    "backgroundImage" : "images/spring/spring_02.jpg",
    "directions" : "(この部分は、アイテム詳細のコンテンツデータとなります) ",
    "group" : {
        "key" : "Spring"
    },
    "key" : 1001,
    "shortTitle" : "春の風景2",
    "dummyPart" : "dummy dummy",
    "title" : "春の風景2"
},
{ 
{ 
    "backgroundImage" : "images/summer/summer_01.jpg",
    "directions" : "(この部分は、アイテム詳細のコンテンツデータとなります) ",
    "group" : {
        "backgroundImage" : "images/summer/summer_group_01.png",
        "description" : "(この部分は、グループデータの説明文となります)",
        "key" : "Summer",
        "shortTitle" : "Summer",
        "title" : "Summer"
    },
    "key" : 2000,
    "shortTitle" : "夏の風景1",
    "dummyPart" : "dummy dummy",
    "title" : "夏の風景1"
},
{ 
    "backgroundImage" : "images/summer/summer_02.jpg",
    "directions" : "(この部分は、アイテム詳細のコンテンツデータとなります) ",
    "group" : {
        "key" : "Summer"
    },
    "key" : 2001,
    "shortTitle" : "夏の風景2",
    "dummyPart" : "dummy dummy",
    "title" : "夏の風景2"
},

(この部分は、省略)

{ 
    "backgroundImage" : "images/winter/winter_15.jpg",
    "directions" : "(この部分は、アイテム詳細のコンテンツデータとなります) ",
    "group" : {
        "key" : "Winter"
    },
    "key" : 4014,
    "shortTitle" : "冬の風景15",
    "dummyPart" : "dummy dummy",
    "title" : "冬の風景15"
}
]

さらに、プロジェクトにDataフォルダを作成し、その配下に上記のsample.txt を配置することにします。

img06_json

次に、このJSONデータを、SampleDataSourceに読み込む処理を記述します。

DataModel/SampleDataSource.csに次のusingを追加します。

using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.Storage;
using Windows.Data.Json;

このSampleDataSourceクラス内に、JSONパース用メソッドを追加します。
メソッド名は任意ですが、ここでは、LoadJsonDataAsync としました。

public static async Task LoadJsonDataAsync() {
	var file = await Package.Current.InstalledLocation.GetFileAsync(@"Data\sample.txt");
	var result = await FileIO.ReadTextAsync(file);
	var data= JsonArray.Parse(result);
	CreateItemsAndGroups(data);	//このあと記述
}

上記で登場している、CreateItemsAndGroupsを記述します。
(上記のLoadJsonDataAsyncメソッド付近でいいでしょう)

private static void CreateItemsAndGroups(JsonArray array) {
	foreach (var dt in array) {
		var obj = dt.GetObject();
		SampleDataItem item = new SampleDataItem("", "", "", "", "", "", null);
		SampleDataGroup group = null;

		foreach (var key in obj.Keys) {
			IJsonValue val;
			if (!obj.TryGetValue(key, out val))
				continue;

			switch (key) {
				case "key":
					item.UniqueId = val.GetNumber().ToString();
					break;
				case "title":
					item.Title = val.GetString();
					break;
				case "shortTitle":
					item.Subtitle = val.GetString();
					break;
				case "directions":
					item.Content = val.GetString();
					break;
				case "backgroundImage":
					item.SetImage(val.GetString());
					break;
				case "group":
					var itemGroup = val.GetObject();
						IJsonValue groupKey;
					if (!itemGroup.TryGetValue("key", out groupKey))
						continue;
						group = _sampleDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(groupKey.GetString()));

					if (group == null)
						group = CreateSingleGroup(itemGroup);
						item.Group = group;
					break;
			}
		}
		if (group != null)
			group.Items.Add(item);
	}
}

上記で登場している CreateSingleGroup はまだ記述していないので、メソッドを記述します。

private static SampleDataGroup CreateSingleGroup(JsonObject obj) {
	SampleDataGroup group = new SampleDataGroup("", "", "", "", "");

	foreach (var key in obj.Keys) {
		IJsonValue val;
		if (!obj.TryGetValue(key, out val))
			continue;

		switch (key) {
			case "key":
				group.UniqueId = val.GetString();
				break;
			case "title":
				group.Title = val.GetString();
				break;
			case "shortTitle":
				group.Subtitle = val.GetString();
				break;
			case "description":
				group.Description = val.GetString();
				break;
			case "backgroundImage":
				group.SetImage(val.GetString());
				break;
		}
	}

	_sampleDataSource.AllGroups.Add(group);
	return group;
}

以上の作業で、SampleDataSourceのデフォルトコンストラクタで行っているダミーデータの生成は不要になりましたので、データを生成している部分(約270行)を、ごっそり削除します。

 public SampleDataSource()
 {
	//この部分は削除するか、コメントにしてよい。
 }

最後に、App.xaml.cs の OnLaunched の最初の行に、次のコードを挿入して完了です。

await Data.SampleDataSource.LoadJsonDataAsync();

screenshot_12242012_034259

ここまでは、ローカルのJSONデータを対象としていましたが、リモート(Webサイト)のJSONデータを取得する場合は、LoadJsonDataAsync の次の2行の部分を変更します。

var file = await Package.Current.InstalledLocation.GetFileAsync(@"Data\sample.txt");
var result = await FileIO.ReadTextAsync(file);

上記のコードを、下記のように変更します。
(httpで始まるURLの部分は、正式なものに書き換えてください)

var client = new System.Net.Http.HttpClient();
client.MaxResponseContentBufferSize = 1024 * 1024;	// 1 MB までと仮定。
var response = await client.GetAsync(new Uri("http://nantoka/kantoka"));
var result = await response.Content.ReadAsStringAsync();

画像ファイルを、ローカルではなくて、Web上のものを使用する場合は、単に、JSON内のデータ(backgroundImageなど)を、httpなどで始まるURLに変更するだけです。

私にとって、生まれて初めてのブログ投稿でしたので、読みづらいところが多々あったかと思います。
「ここは、こんな風にするといい」というようなご指摘ありましたら、遠慮なくコメントいただけるとうれしいです!