ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Windows ストアアプリ (JavaScript で開発) におけるアプリ設定の表示方法

Windows 8 で新たに導入される Windows ストアアプリ (旧称 Metro スタイルアプリ) の開発に関する話です。

この記事では、Windows ストアアプリの設定チャームからアプリの設定を行えるように、以下の内容について説明します。 なお、JavaScript で Windows ストアアプリを開発する場合についてのみ取り扱います。

  • 設定ウィンドウにアプリ設定のエントリポイントを追加する方法
  • 設定ポップアップ (settings flyout) を表示する方法 (SettingsFlyout コントロールの使い方)
  • SettingsFlyout コントロールを開くエントリポイントを簡単に追加するための便利関数

アプリ設定の指針については以下をご覧ください。

本記事を読むために必要な前提知識

設定ウィンドウに項目を追加する方法 (SettingsCommand エントリポイントを追加する方法)

f:id:nobuoka:20121003002211p:plain

設定チャームから設定ウィンドウを開くと、デフォルトでは 「アクセス許可」 という項目のみがアプリ設定の一覧のところに表示されます。 ここにアプリ設定の項目を追加するには、設定ウィンドウが開く直前に設定ウィンドウ上で発火する CommandsRequested イベント を捕捉し、その中でアプリ設定のエントリポイントの追加を行う必要があります。

設定ウィンドウを表すオブジェクト (SettingsPane オブジェクト) に CommandsRequested イベントを捕捉するリスナを登録する

設定ウィンドウを表すクラスは Windows.UI.ApplicationSettings.SettingsPane であり、現在のビューにおける設定ウィンドウを表すオブジェクトは Windows.UI.ApplicationSettings.SettingsPane.getForCurrentView メソッド で取得できます。 このオブジェクト上で発生する CommandsRequested イベントを捕捉するリスナの登録は、以下のようになります。

var settingsPane = Windows.UI.ApplicationSettings.SettingsPane.getForCurrentView();
settingsPane.addEventListener("commandsrequested", function (evt) {
    // ... この中でアプリ設定のエントリポイントを追加する ...
});

なお、このイベントリスナに渡される引数は、SettingsPaneCommandsRequestedEventArgs オブジェクト です。

CommandsRequested イベントを捕捉するリスナの中でアプリ設定のエントリポイントを追加する

そして、設定ウィンドウにアプリ設定の項目を追加するために、CommandsRequested イベントのリスナに渡される引数 evt の プロパティ evt.request.applicationCommands にアプリ設定のエントリポイントを表すオブジェクトを追加する必要があります。 エントリポイントを表すオブジェクトは SettingsCommand オブジェクト です。

上で例を示したイベントリスナの登録も含めて例を出すと、以下のようになります。

var settingsPane = Windows.UI.ApplicationSettings.SettingsPane.getForCurrentView();
settingsPane.addEventListener("commandsrequested", function (evt) {
    var cmd = new Windows.UI.ApplicationSettings.SettingsCommand( "commandId", "Title", function (cmd) { console.dir(cmd) } );
    evt.request.applicationCommands.append(cmd);
    // 当然ながら evt.request.applicationCommands へは SettingsCommand オブジェクトを複数個追加できる
});

上のコードを実行したうえで設定ウィンドウを開くと、設定ウィンドウには "Title" というアプリ設定の項目が表示されるはずです。 そして、その項目を選択すると console.dir(cmd) という処理が実行され、デバッグ時であればコンソールに cmd の中身が表示されます。

別の方法

さらに、Windows.UI.ApplicationSettings.SettingsPane.getForCurrentView() のオブジェクトで CommandsRequested イベントが発火した場合、それを捕捉して WinJS.Application で settings イベントが発生するようになっています。 そのため、以下のように WinJS.Application の settings イベントのリスナを登録することでも、大体似たようなことができます。 リスナに渡されるイベントオブジェクトは同じものではないので注意が必要です。

// Windows.UI.ApplicationSettings.SettingsPane.getForCurrentView() のオブジェクトで
// CommandsRequested イベントが発生するとこっちが発火するようになっている.
// CommandsRequested イベントに渡されるイベントオブジェクトが, こちらの evt.detail.e に格納されている
WinJS.Application.addEventListener("settings", function (evt) {
    var cmd = new Windows.UI.ApplicationSettings.SettingsCommand( "commandId", "Title", function (cmd) { console.dir(cmd) } );
    evt.detail.e.request.applicationCommands.append(cmd);
});
// addEventListener を使わずに WinJS.Application.onsettings プロパティにリスナを代入してもよい

設定ポップアップの表示 (SettingsFlyout コントロールの使用)

上の例では、設定ウィンドウの "Title" という設定項目を選択するとコンソールに出力されるようになっていましたが、実際には設定ポップアップを表示させる必要があります。

f:id:nobuoka:20121003005721p:plain

JavaScript で Windows ストアアプリを開発している場合は、SettingsFlyout コントロール を用いることで設定ポップアップを簡単に作成できます。 HTML で SettingsFlyout コントロールを定義する方法としては典型的には以下のようになります。

<div id="settings-flyout-example" data-win-control="WinJS.UI.SettingsFlyout" data-win-options="{settingsCommandId:'commandId',width:'wide'}">
  <!-- クラスには 'win-ui-light' か 'win-ui-dark' のどちらかを選択すれば良い (しなくてもいいと思われる) -->
  <div class="win-ui-dark win-header" style="background-color:#00b2f0"><!-- ヘッダの背景色はアプリのタイルの色に準ずること (ガイドラインより) -->
    <!-- 設定ウィンドウを表示するボタン -->
    <button type="button" onclick="WinJS.UI.SettingsFlyout.show()" class="win-backbutton"></button>
    <div class="win-label">設定ポップアップのタイトル</div>
  </div>
  <div class="win-content ">
    <div class="win-settings-section">
      <h3>XXX の設定</h3>
        <p>...</p>
      </div>
  </div>
</div>

上で定義した SettingsFlyout コントロールを表示するには、以下のようにコントロールを取得して show メソッドを呼び出すだけです。

var settingsFlyoutExample = document.getElementById("settings-flyout-example").winControl;
settingsFlyoutExample.show();

SettingsFlyout を表示するための便利関数

上の例では SettingsFlyout コントロールを取得して表示しましたが、別の HTML で定義した SettingsFlyout コントロールを ID 指定で表示させることができる便利なメソッドがあります。

// /html/3-SettingsFlyout-Legal.html で定義された legalNotices という ID の SettingsFlyout コントロールを表示する
WinJS.UI.SettingsFlyout.showSettings( "legalNotices", "/html/3-SettingsFlyout-Legal.html" );

実装をみてみたところ、指定した HTML ファイルを Page コントロールとして現在のドキュメントに読み込み、指定の id を持つ SettingsFlyout コントロールを取得して表示する、ということをしていました。 (すでに現在のドキュメント中に指定の id の SettingsFlyout コントロールがあれば、指定の HTML ファイルを Page コントロールとして読み込むことはしない。) 便利ですね。 ちなみにここでいう id は、Element ノードの id ではなく、SettingsFlyout の settingsCommandId なのですが、存在しない場合は Element ノードの id もみるようになっていました。

SettingsFlyout を表示するアプリ設定のエントリポイントを追加する便利関数

ここまでみてきたエントリポイントの追加の方法と SettingsFlyout の表示の方法を組み合わせることで、SettingsFlyout を表示するアプリ設定のエントリポイントを追加することができます。

しかし、もっと簡単にするために WinJS.UI.SettingsFlyout.populateSettings メソッド を使うことができます。

WinJS.Application.addEventListener("settings", function (evt) {
    evt.detail.applicationcommands = {
        // プロパティ名は SettingsFlyout コントロールの ID (settingsCommandId)
        // title は設定ウィンドウの項目に表示される
        // href はその SettingsFlyout コントロールが定義されている HTML ファイル
        help: { title: "Help", href: "/html/2-SettingsFlyout-Help.html" },
        name: { title: ..., href: ... },
        // ...
    };
    WinJS.UI.SettingsFlyout.populateSettings(evt);
});

ただし、実装をみてみたところ、ここで指定した applicationcommands はグローバルに 1 つ保持されるだけなので、1 回の設定チャーム起動のイベントの間に複数回の populateSettings 呼び出しがあると最後の呼び出しのものしか保持されずにおかしくなります。 (SettingsCommand エントリポイントの追加は全部行われるが、その実行ができるのは最後の populateSettings 呼び出しで追加したもののみになってしまう。)

つまり、以下のようにすると、設定ウィンドウに "Help" と "Legal Notices" が表示されるが、あとから追加した "Legal Notices" しか押しても反応しないようになってしまいます。 微妙な仕様ですね。

WinJS.Application.addEventListener( "settings", function (e) {
    e.detail.applicationcommands = { help: { title: "Help", href: "/html/2-SettingsFlyout-Help.html" } };
    WinJS.UI.SettingsFlyout.populateSettings(e);
});
WinJS.Application.addEventListener("settings", function (e) {
    e.detail.applicationcommands = { legalNotices: { title: "Legal Notices", href: "/html/3-SettingsFlyout-Legal.html" } };
    WinJS.UI.SettingsFlyout.populateSettings(e);
});

設定ウィンドウをプログラム側から表示させる方法

ちなみに、設定ウィンドウはユーザーが設定チャームを呼び出したときだけでなく、プログラム側からも表示させることができます。

WinJS.UI.SettingsFlyout.show();

まとめ

長々と書いてしまいましたが、アプリ内のどの場所でも設定ウィンドウに表示する項目は同一で、項目がクリックされたときには SettingsFlyout コントロールが表示されればよい、ということであれば、最後に紹介した WinJS.UI.SettingsFlyout.populateSettings メソッドを使う方法を用いるのが良いかと思います。

単純に SettingsFlyout コントロールを表示すればよいというわけではない (例えば設定ウィンドウのヘルプの項目をクリックした時に web ページを表示させるなど) のであれば、上の方で紹介した方法を用いる必要があります。