使ってみよう! Bing API/SDK

第8回 Hello, Bing Map App!──Silverlightで作るBing Mapsアプリケーション(8)

この記事を読むのに必要な時間:およそ 8 分

設定の保存

最後に新しいContractを紹介します。Map Appの状態や設定を保存できるようにしてみましょう。

分離ストレージ

.NET FrameworkやSilverlightでは,設定などを保存するために分離ストレージという仕組みが用意されています。分離ストレージを使用すると,ユーザーおよびアプリケーション別にデータが分離され,アプリケーションは自アプリケーションのデータにのみアクセスできます。また,Silverlightのようにユーザーのファイルにアクセス権限がない低い権限の場合も分離ストレージを利用できます。

Map Appでもこの分離ストレージが利用できます。ただし,通常のSilverlightアプリケーションとは異なり,Contractによって提供されたものを利用します。プラグインのクラス,MyPhotoPluginに,これまでの連載と同様に次のようにプロパティを宣言して使います。

MyPhotoPlugin.cs

[ImportSingle("Microsoft/IsolatedStorageContract", ImportLoadPolicy.Synchronous)]
public IsolatedStorageContract IsolatedStorageContract { get; set; }

分離ストレージを使用したデータは,クライアント領域に通常は保存されます。そのためユーザーが異なるPCからアプリケーションを実行した場合,当然ながら別PCで保存した内容は参照できません。

My Photo Map Appに追加された写真の保存

今回のアプリケーションでは分離ストレージに,追加した写真の情報を保存して,次回実行時も以前追加した写真が参照できるようにしてみましょう。ただし,削除処理や利用可能なストレージ容量のチェックなどは実装していませんので,Map Appでの分離ストレージの利用サンプルとして使用してください。

保存する内容は,写真ファイルをそのまま保存するのではなくPhotoEntityオブジェクトを後から生成できる次の内容を保存します。

  • ID
  • 名前
  • 経緯度
  • サムネイル画像

サムネイル画像は写真ごとに別のファイルとして,それ以外はひとつのXMLファイルとして保存するようにします。

分離ストレージにファイルを作成するには,IsolatedStorageContract.GetPluginSpecificIsolatedStorageFileメソッドを使用します。このメソッドは何度も使うため,MyPhotoPanelクラス内にプロパティとして次のように参照できるようにしておきましょう。

private PluginIsolatedStorageFile IsolatedStorageFile
{
    get
    {
        return this.plugin.IsolatedStorageContract.GetPluginSpecificIsolatedStorageFile(this.plugin.Token);
    }
}

ちなみにファイルを作成して設定を保存する方法以外にもPluginIsolatedStorageSettingsクラスを使用した方法もあります。次のように,設定をキーで参照できます。

var settings = this.plugin.IsolatedStorageContract.GetPluginSpecificIsolatedStorageSettings(this.plugin.Token);

// 設定の保存
settings["key"] = "value";

// 設定の参照
string value;
if (settings.TryGetValue<string>("key", out value))
{
    // Do something
}

保存処理

それでは,保存部分をMyPhotoPanelクラス内に記述します。作成するXMLファイルは次のような構成にします。写真の数だけ,<photos>要素の中に<photo>要素があります。各<photo>要素に対応するサムネイルの画像は,画像ファイル名にIDの値を使用し,参照するものとします。

<?xml version="1.0" encoding="utf-8"?>
<photos>
        <photo id="1a1618d9-a138-4f61-9161-278dd6d5c65c"
               name="DSC00001.JPG"
               latitude="36.087925"
               longitude="139.7249733" />
        <photo id="a03D363a-7a2B-42fc-8d53-f3e78a59d2b6"
               name="DSC00002.JPG"
               latitude="36.077934"
               longitude="139.719575" />
</photos>

XMLファイルを作成する部分のメソッドは次のように記述します。

private void Save()
{
    var stream = IsolatedStorageFile.CreateFile("photos.xml");
    var writer = System.Xml.XmlWriter.Create(stream);
    writer.WriteStartDocument();
    writer.WriteStartElement("photos");

    foreach (var item in PhotoItems)
    {
        writer.WriteStartElement("photo");

        writer.WriteStartAttribute("id");
        writer.WriteString(item.Id);
        writer.WriteEndAttribute();

        writer.WriteStartAttribute("name");
        writer.WriteString(item.Name);
        writer.WriteEndAttribute();

        // 経緯度の書き込み
        var pin = (PointPrimitive)item.Primitive;
        writer.WriteStartAttribute("latitude");
        writer.WriteString(pin.Location.Latitude.ToString());
        writer.WriteEndAttribute();

        writer.WriteStartAttribute("longitude");
        writer.WriteString(pin.Location.Longitude.ToString());
        writer.WriteEndAttribute();

        writer.WriteEndElement(); // </photo>
    }

    writer.WriteEndElement(); // </photos>
    writer.Close();
    stream.Close();
}

このSaveメソッドを呼び出す場所は,ボタンクリック時(Button_Click)とリストボックスのドロップ処理時(PhotoListBox_Drop)の2か所です。各メソッドの最後に呼出しの記述を追加してください。

後は,サムネイル画像の保存処理部分です。これはCreateEntityメソッド内で行うことにしましょう。サムネイルを取得した時点で次のように保存します。

// 画像の読み込み
var img = new BitmapImage();
using (var ms = new MemoryStream(info.ThumbnailData))
{
    img.SetSource(ms);

    // (以下を追記)
    // ID の名前で画像を保存
    using (var f = IsolatedStorageFile.CreateFile(entity.Id))
    {
        ms.WriteTo(f);
    }
}

保存部分は以上です。分離ストレージに保存したファイルは,ローカルのユーザーデータ保存領域にあります。既定であれば,C:\ユーザー名\Owner\AppData\LocalLow\Microsoft\Silverlight フォルダーのどこかに保存されています。デバッグ時にファイルを参照したい場合は,検索でphotos.xmlを探してみるとよいでしょう。

読み取り処理

実行時に,保存したphotos.xml からPhotoEntiyオブジェクトを生成して,アプリケーションに反映させます。読み取り処理部分を行うメソッドを次のようにMyPhotoPanel内に記述します。アクセス修飾子がpublicであることに注意してください。

public void Load()
{
    if (!this.IsolatedStorageFile.FileExists("photos.xml"))
    {
        return;
    }

    try
    {
        var stream = this.IsolatedStorageFile.OpenFile("photos.xml", FileMode.Open);
        var reader = System.Xml.XmlReader.Create(stream, new System.Xml.XmlReaderSettings());
        
        while (reader.Read())
        {
            if (reader.NodeType == System.Xml.XmlNodeType.Element &&
                reader.LocalName == "photo")
            {
                var entity = new PhotoEntity();

                reader.MoveToAttribute("id");
                entity.Id = reader.Value;

                reader.MoveToAttribute("name");
                entity.Name = reader.Value;

                var location = new Location();
                reader.MoveToAttribute("latitude");
                location.Latitude = Convert.ToDouble(reader.Value);
                reader.MoveToAttribute("longitude");
                location.Longitude = Convert.ToDouble(reader.Value);

                // プッシュピン作成
                entity.Primitive = this.plugin.PushpinFactoryContract.CreateStandardPushpin(location);

                // 画像読み込み
                entity.BitmapImage = new BitmapImage();
                entity.BitmapImage.SetSource(IsolatedStorageFile.OpenFile(entity.Id, FileMode.Open, FileAccess.Read));

                this.plugin.MainLayer.Entities.Add(entity); // レイヤーに追加
                PhotoItems.Add(entity); // コレクションに追加
                RegisterPopup(entity); // ポップアップ登録
            }
        }
    }
    catch (Exception)
    {
        // Do nothing
    }
}

このLoadメソッドを呼び出すタイミングですが,レイヤーに追加処理などを行っているため注意が必要です。今回のクラス構造では,レイヤー生成時にパネルの生成をしています。パネル生成時にレイヤーにエンティティを追加しようとすると例外が発生します。そこで今回は,プラグインのInitializeメソッド内で次のようにLoadメソッドを呼びます。

MyPhotoPlugin.cs

public override void Initialize()
{            
    base.Initialize();
    this.MainLayer = new MyPhotoLayer(this.Token, this);
    ((MyPhotoPanel)this.MainLayer.Panel).Load(); // 追加
}

以上でMy Photo Map Appの完成です。実行して動作を確認してみてください。うまく動いたでしょうか?

おわりに

My Photo Map Appの作成は,いかがでしたでしょうか。ちょうどMap App SDKの更新もあり,開発が用意になっていますのでぜひ作成してMap Appを登録してみてください。今回でMap App開発の紹介は一段落して,次回からは別のAPI/SDKを紹介していく予定です。

著者プロフィール

松江祐輔(まつえゆうすけ)

日本システムウエア株式会社 勤務。現在,ハードウェア設計・検証業務を担当。大学生・大学院生時代はベンチャー企業 有限会社ミレニアムシステムズにプログラマーとして従事。趣味はプログラミング。好きな言語はVisual Basic。Microsoft MVP for Windows Live Platform(Jul 2010 - Jun 2011),Windows Live(Jul 2011 - Jun 2013)。

URL:http://katamari.jp