連載
» 2014年01月10日 13時35分 公開

連載:Windowsストア・アプリ開発入門:第9回 効果的に情報を提示する (5/7)

[山本康彦(http://www.bluewatersoft.jp/),BluewaterSoft]

セカンダリタイル機能を作る

 記事一覧画面に「ピン留めする」ボタンを設置し、エンドユーザーがセカンダリタイルを作成できるようにしよう。

セカンダリタイル機能のスペックを考える

 記事一覧画面にアプリバーを追加し、そこに[ピン留めする]ボタンを設置しよう。[ピン留めする]ボタンは[ピン留め解除]ボタンも兼ねる。また、[ピン留めする]ボタンは、アプリバーの右に表示しよう*5

*5アプリバーの左右に配置するボタンの振り分けは、MSDNの「コマンド パターン」を参考にしてほしい。[ピン留めする]ボタンを左右どちらに置くかは悩ましい(MS製アプリでもばらついている)のだが、メインページを除く全画面に共通する既定のコマンドだと考えて右側に配置した。


 セカンダリタイルの機能を作るには、どのようなタイルを提供するかと、パラメーターのフォーマットを決めておかねばならない。

 まず、提供する画像であるが、基本的には既定のタイルと同じにしよう。ただし、中タイルだけは、テキストを表示する余地がないので、新しく画像を用意する(次の画像)。

既定の中タイル(左)とセカンダリタイル用の中タイル(右) 既定の中タイル(左)とセカンダリタイル用の中タイル(右)
タイルの下部にテキストを表示するため、セカンダリタイル用には別の画像を用意した。
どちらも背景色は透明であるが、図形が見えるように緑色に着色してある。
このセカンダリタイル用の中タイルの画像は、プロジェクトのAssetsフォルダーに「Logo2nd.scale-100.png」というファイル名で保存し、ビルドアクションを[コンテンツ]にしておく。

 ちなみに、「ピン留めする」ときにセカンダリタイルを最大4種類まで提示できる。詳しくは「WinRT/Metro TIPS:Windows 8.1の新機能、セカンダリ・タイル作成時の代替ロゴを使うには?[Windows 8.1ストア・アプリ開発]」を参照してほしい。

 次に、セカンダリタイルを作るときのパラメーターを決める。セカンダリタイルをタップされたときの動作や、セカンダリタイルの削除時に必要になる情報なので、しっかり考えておく必要がある。今回は次の表のように決めた。

名称 役割 制限事項など スペック
TileId セカンダリタイルを識別する。削除するときにも使う 64文字以内の特定の文字(a〜z、A〜Z、0〜9、ピリオド、アンダースコア) フィードのタイトルを加工して、その先頭64文字までを使う。
加工:URLエンコードし、さらにスペースを%20に置換
DisplayName セカンダリタイルの下部に表示されるテキスト 256文字まで フィードのタイトルをそのまま使う
Arguments セカンダリタイルがタップされると、AppクラスのOnLaunchedメソッドの引数として渡される 2048文字まで "FeedPage,{URLエンコードしたフィードのタイトル}"

このパラメーターだけでアプリの動作を決定しないといけない。そこで、カンマ区切りの文字列とし、1カラム目は表示すべき画面の名前とした。FeedPage(=記事一覧画面)の場合は、2カラム目にはフィードのタイトルを入れることとする
RoamingEnabled 作成したセカンダリタイルがローミングされるかどうか 既定値はtrue 既定値のまま(true)とする
セカンダリタイル作成時のパラメーター

セカンダリタイル機能を実装する

 今回は、「ピン留め」ボタンの表示切り替え機能を持ったコントロールを作り、それを記事一覧画面のアプリバーに配置し、そしてセカンダリタイルを作成するコードを実装する。最後に、セカンダリタイルをタップされたときの動作を追加する。

「ピン留め」ボタンの表示切り替え

 実装に当たって意外と面倒なのは、「ピン留めする」と「ピン留め解除」のボタンの切り替えだ。2つのボタンを並べておいて片方の表示を隠すようにしてもよい。だが今回は、AppBarButtonコントロール(Windows.UI.Xaml.Controls名前空間)を継承して独自のコントロールを作り、その中で表示するアイコンを切り替えるようにしてみよう。

 プロジェクトの「Tile」フォルダー(ライブタイルの実装で作成した)に新しいクラスを「PinButton.cs」というファイル名で作成する。そうしたら、PinButtonクラスにAppBarButtonコントロールを継承させる。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Windows.UI.Xaml.Controls;

namespace AtmarkItReader.Tile
{
  public class PinButton : AppBarButton
  {
  }
}

PinButtonクラスを作り、AppBarButtonコントロールを継承させる(C#)
自動生成されたコードに太字の部分を追加する。
AppBarButtonコントロールを継承させたので、この状態のPinButtonクラスはAppBarButtonコントロールと全く同じ振る舞いをする。この後、機能を追加していく。

 このPinButtonコントロールを記事一覧画面のアプリバーに配置すると、データコンテキストにはFeedオブジェクト(=1つのRSSフィードの情報)がバインドされる。すると、セカンダリタイルに必要となるTileIdなどのパラメーターは、PinButtonコントロール内でデータコンテキストから作り出せる(次のコード)。

private string FeedTitle
{
  // データコンテキストからフィードのタイトルを取り出す
  get { return (this.DataContext as DataModel.Feed).Title; }
}

// TileIdを得る
private string TileId
{
  get
  {
    return System.Net.WebUtility.UrlEncode(FeedTitle)
            .Replace("+", "%20")
            .Head(64);  // HeadメソッドはStringExtension内
  }
}

// Argumentsを得る
private string Arguments
{
  get
  {
    return string.Format("FeedPage,{0}",
                          System.Net.WebUtility.UrlEncode(FeedTitle)
                        );
  }
}

// DisplayNameを得る
private string DisplayName
{
  get
  {
    return FeedTitle.Head(256);
  }
}

PinButtonクラスでセカンダリタイルのパラメーターを作る(C#)
セカンダリタイルに必要となるTileId/Arguments/DisplayNameのパラメーターを生成するコード。
Headメソッドについては、次に掲載する。

 上のコード中にあるHeadメソッドは、文字列の先頭から最大で引数で指定された文字数までを取り出すものだ。これはstringクラスを拡張するメソッドで、プロジェクトの「Logic」フォルダーに新しいクラスを「StringExtension.cs」というファイル名で作成し、そこに次のようなコードで実装している。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AtmarkItReader
{
  public static class StringExtension
  {
    public static string Head(this string s, int maxLength)
    {
      if (string.IsNullOrEmpty(s))
        return s;

      if (s.Length <= maxLength)
        return s;

      return s.Substring(0, maxLength);
    }
  }
}

新設したStringExtensionクラスのHead拡張メソッド(C#)
上のコードで使っているHead拡張メソッドである。
「Logic」フォルダーに置いたが、名前空間は「AtmarkItReader.Logic」とせず、「AtmarkItReader」としている。これは、いちいちusingしなくてもプロジェクト内でこのメソッドを使えるようにするためだ。

 さて、TileIdが取得できるようになったので、該当するセカンダリタイルがすでに表示されているかどうかを判定できる。それには、SecondaryTileクラス(Windows.UI.StartScreen名前空間)のExistsメソッドを使う。PinButtonコントロールがロードされたときに、これから「ピン留め」するのか解除するのかを判定し、それに基づいてアイコンとラベルを切り替えるようにしよう(次のコード)。

public class PinButton : AppBarButton
{
  // 「ピン留めする」ときのアイコンとテキスト
  private readonly IconElement _pinIcon = new SymbolIcon(Symbol.Pin);
  private const string _pinLabel = "ピン留めする";

  // 「ピン留め解除」のときのアイコンとテキスト
  private readonly IconElement _unPinIcon = new SymbolIcon(Symbol.UnPin);
  private const string _unPinLabel = "ピン留め解除";

  public PinButton()
  {
    base.Icon = _pinIcon;
    base.Label = _pinLabel;

    // コンストラクターでLoadedイベントのハンドラーを結び付ける
    base.Loaded += PinButton_Loaded;
  }

  void PinButton_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
  {
    // アイコンとラベルを設定する
    DetermineState();
  }



  // これからピン留めするのか外すのかを示すフラグ
  private bool _isUnPin;

  // アイコンとラベルを設定する
  private void DetermineState()
  {
    // すでにセカンダリタイルが表示されているなら、「ピン留め解除」
    _isUnPin = Windows.UI.StartScreen.SecondaryTile.Exists(TileId);

    if (_isUnPin)
    {
      base.Icon = _unPinIcon;
      base.Label = _unPinLabel;
    }
    else
    {
      base.Icon = _pinIcon;
      base.Label = _pinLabel;
    }
  }
  ……省略(前述の3プロパティ)……

PinButtonコントロールにロード時に表示を切り替えるコードを追加する(C#)
データコンテキストが正しくバインドされていないと、Existsメソッドを呼び出している行で例外が発生する。気になるようなら、try〜catchを追加してほしい。

 アプリバーが開くときにPinButtonコントロールのLoadedイベントが発生するので、そのタイミングでアイコンとラベルが切り替わる。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。