Windows8 CPでMetroアプリを作る その3 「チャームから検索できるようにする」

この記事ではMSDNに記載されているJavaScriptで作る初めてのMetroスタイルアプリを使ってチャームからの検索を実行できるようにします。

コントラクト

Metroスタイルアプリではアプリ間の情報のやり取りや外部からのアプリ呼び出しを「コントラクト」と呼ばれる仕組みで制御しています。Metroスタイルアプリの各プロセスはサンドボックスになっているため、コントラクトを利用して情報の共有をおこなっていると言い換えることもできます。

今回はチャームからアプリ内のデータを検索できる検索コントラクトを実装します。この機能を実装することでユーザーはWindows8のシステム内のどこからでも、アプリの情報を検索できることになります。

検索コントラクトの追加

コントラクトの追加は「マニフェストの編集」→「コードの追加」→「機能の実装」という順番で行います。VisualStudioを使用している場合は以下のような手順で「マニフェストの編集」と「コードの追加」を同時に行うことができます。

  • プロジェクトの先頭で右クリックし、「追加」→「新しいアイテム」を選択します
  • 表示されたダイアログの中から「検索コントラクト」を選択します

プロジェクトにsearch.html, search.js, search.cssが追加され、マニフェストにExtension·Category="windows.search"が追記されます。

マニフェストに検索コントラクトが追加されたことで、チャームの「検索」からこのアプリケーションが指定できるようになります。
F5キーを押してデバッグを開始してみましょう。チャームを出して「検索」を選択すると、検索候補のアプリケーション一覧の中にこのアプリケーションが表示されるようになっています。

ナビゲーションの追加

実際にチャームからの操作に対応するには、アプリの起動(アクティベーション)時にアクティベーションの種類を判断し、検索アクティベーションであれば検索機能を呼び出すような実装をする必要があります。具体的にはactivatedイベントにイベントリスナーを登録してアクティベーションの種類によって転送先のhtmlを変更するようにします。このサンプルアプリではdefault.jsに記述します。

app.onactivated = function (eventObject) {
    var detail = eventObject.detail;
    var uri;
    var params
    if (detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
        //中略

    //追記ここから
    } else if (detail.kind === Windows.ApplicationModel.Activation.ActivationKind.search) {
        uri = "/html/search.html";
        params = {queryText: detail.queryText};
    }
    if (uri) {
        eventObject.setPromise(Win.UI.processAll().then(function(){
            return WinJS.Navigation.navigate(uri, params);
        }));
    }
    //追記ここまで
};

search.jsの修正と起動確認

あとはsearch.js, search.htmlでバインドするパラメータを修正すればとりあえず検索コントラクトを利用することができます。具体的には以下のような修正をおこないます。

  • search.jsのsearchData関数の中でgrep対象になっているパラメータを修正する(item.subtitleとかはないはずですよね)
  • search.htmlのdiv .item-content h4でバインドしているパラメータを修正する(item.subtitleとか以下略)

修正がおわったらF5を押してアプリケーションを起動してみます。チャームから「検索」を選択し、Windowsなどの単語でサンプルアプリを検索すると、該当するブログ記事の一覧が表示されます。

おまけ:フィルターの編集

いままで行った変更でとりあえず検索コントラクトが利用できるようになりましたが、画面上部のフィルターが正常に動作していないと思います。ブログのフィードごとのフィルターを登録するには以下のような変更をsearch.jsに加えます。

  • generateFilters関数の余計なフィルター生成を削除する(Group1とか)
  • data Namespaceからフィードのデータを取得し、タイトルごとのフィルターを作成する

たとえば以下のようになります。

if (typeof window.data !== 'undefined') {
    data.groups.forEach(function(feed){
        this.filters.push({ resulte: null, text: feed.title, predicate: function(item) { return item.group.key === feed.key;}});
    });
}