特集:続・Windows 8開発に向けて準備しよう

「コントラクト」でMetroスタイル・アプリのサンドボックスを乗り越える!
―― アプリを連携させる新しい仕組みを理解しよう! ――

BluewaterSoft 山本 康彦
2012/05/11
2012/10/25 更新
Page1 Page2

ファイル・アクティベーション

 ファイル拡張子に関連付けられたアプリを起動する。なお、デスクトップ・アプリが関連付けられている場合は、デスクトップに切り替わって起動される。

送り側

StorageFile file = ……;
await Launcher.LaunchFileAsync(file);
List 1-1: ファイル拡張子に関連付けられたアプリを起動する

受け側

必要な宣言:ファイルの種類の関連付け (必須項目: 表示名、名前、ファイルの種類(「.txt」など))

 アプリ起動時に、通常のOnLaunchedメソッドではなくOnFileActivatedメソッドが呼び出される。引数には、送り側がセットしたStorageFileオブジェクトが入っているので、通常のOnLaunchedメソッドで行う処理のほかに、StorageFileオブジェクトを処理するコードを記述する(次のコードを参照)。

protected async override void OnFileActivated(FileActivatedEventArgs args) {

  var fileList = args.Files;
  foreach (StorageFile file in fileList)
  {
    using (Stream s = await file.OpenStreamForReadAsync()) {
      // ファイルの読み込みを行う
    }

  }

  // 通常のOnLaunchedメソッドと同様の処理を記述

List 1-2: ファイル拡張子に関連付けられたアプリが起動される(App.xaml.cs)

 なお、このようにしてファイルの種類の関連付けがされていると、検索ペインでファイルを検索したときにも起動される。

 このサンプル・コードではOnFileActivatedメソッド中でファイルを読み込んでいるが、一般的には起動時間短縮のために、画面を表示してから読み込むようにする。

プロトコル・アクティベーション

 このプロトコルとは、「http:〜」や「ms-help:〜」といった形で、Uriクラスが受け付けるものなら何でもよい。独自のプロトコルを定義しても構わない。

送り側

 ここでは、独自に決めた「bwprotocol」プロトコルを使ってみる。

var uri = new Uri("bwprotocol:TEST?MSG=Hello,%20Metro!!");
await Launcher.LaunchUriAsync(uri);
List 2-1: プロトコルに関連付けられたアプリを起動する

受け側

必要な宣言:プロトコル(必須項目: 名前(ここでは「bwprotocol」))

 プロトコル・アクティベーションには、専用のActivatedメソッドはなく、汎用のOnActivatedメソッドが呼び出される。引数には、送り側がセットしたIActivatedEventArgsオブジェクトが入っているので、そのKindプロパティを調べて、どのコントラクトで起動されたかを判断する(次のコードを参照)。ファイル・アクティベーションと同様、通常のOnLaunchedメソッドで行う処理の記述も必要である。

protected override void OnActivated(IActivatedEventArgs args)
{
  if (args.Kind == ActivationKind.Protocol)
  {
    var protocolArgs = args as ProtocolActivatedEventArgs;
    var url = protocolArgs.Uri.ToString();
    // 変数「url」には「bwprotocol:TEST?MSG=Hello,%20Metro!!」が入る。
    string query = protocolArgs.Uri.Query;
    // 変数「query」には「MSG=Hello,%20Metro!!」が入る。
    // プロトコルに応じた処理を行う。
  }

  // 通常の OnLaunchedメソッドと同様の処理を記述

List 2-2: プロトコルに関連付けられたアプリが起動される(App.xaml.cs)

共有コントラクト

 ここでは、送り側のみ紹介する。ファイル・アクティベーションなどとは異なり、共有コントラクトでは送り側にもイベント・ハンドラが必要である。

送り側: 共有ペインを開く

 ユーザーが共有したいと思ったときに自分で共有チャームを使って開くので、実際にはこのような実装は必要ない。参考までに共有ペインを開く方法を掲載しておく。

DataTransferManager.ShowShareUI();
List 3-1: 共有ペインを開く

 このDataTransferManagerクラスが、共有コントラクトで必要になるやりとりを、Metro実行環境との間で仲立ちしてくれる。

送り側: DataRequestedイベント・ハンドラの追加と削除

 DataTransferManagerクラスは、共有ペインが開かれたときに、共有すべきオブジェクトを送り側のアプリに対して要求してくる。その要求を処理するのがDataRequestedイベント・ハンドラである(次のコードを参照)。このイベント・ハンドラが存在しないと、共有ペインは「○○(=アプリ名)は共有できません」と表示して、共有は失敗する。

private void AddDataRequestedHandler()
{
  DataTransferManager.GetForCurrentView().DataRequested
    += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.DataRequested);
}

private void RemoveDataRequestedHandler()
{
  DataTransferManager.GetForCurrentView().DataRequested
    -= new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.DataRequested);
}
List 3-2: DataRequestedイベント・ハンドラを追加/削除するメソッドを実装する

 このイベント・ハンドラの追加と削除は、適切に行わねばならない。重複して追加や削除をすると例外になるし、ユーザーが共有したいと思った場面ではイベント・ハンドラが追加されていなければならない。

 一般的にはApp.xaml.csファイルのOnLaunchedメソッドとOnXxxActivatedメソッド(例:OnActivatedメソッド)の中でイベント・ハンドラを追加する(その場合は、削除することは考えなくてよい)。共有できるアイテムを持っている画面だけでイベント・ハンドラを有効にしたい場合は、その画面のOnNavigatedToメソッドで追加し、OnNavigatedFromメソッドで削除するとよいだろう。

送り側: DataRequestedイベント・ハンドラの実装

 上記のList 3-2でイベント・ハンドラとして追加/削除しているDataRequestedメソッドは、次のようになる。引数が持っているRequest.Dataプロパティに、共有させたいオブジェクトの情報をセットする。

private void DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
  var item = this.flipView.SelectedItem as SampleDataItem;
  var fileInfo = item.FileInfo; // 画面上で選択されているアイテムのFileInformationオブジェクト

  var req = args.Request;
  req.Data.SetStorageItems(new FileInformation[]{ fileInfo, });
  req.Data.Properties.Title = "MetroContractSample";
  req.Data.Properties.Description = "選択したファイルを共有コントラクトに送ります";
}
List 3-3: DataRequestedイベント・ハンドラの実装

 ここではファイルを共有したいのでSetStorageItemsメソッドを使った。ほかに、SetBitmap、SetData、SetTextなどのメソッドが用意されている。

送り側: 画面の例

 実際の画面は、次のようになる。ShowShareUIメソッドを呼び出すと共有ペインが表示される。表示されるまでの間にDataRequestedイベントも発生しており、共有ペインが表示されたときには、共有すべきオブジェクトはすでに渡されている。

共有ペイン(右側)を表示
共有ペインに表示されている文字列は、DataRequestedイベント・ハンドラ内でセットされたものになっている。

 ここでユーザーが共有先としてメールを選択すると、共有ペインの中はメール・アプリに切り換わる。ユーザーがメール・アプリを選択したことは、送り側には分からない。

共有ターゲットの表示
共有ペインの中がメール・アプリに切り換わる。送り側から提供されたファイルが、メールの添付ファイルになっていることが分かる。

受け側

必要な宣言:共有ターゲット(必須項目: サポートされるファイルの種類、または、データ形式)

 説明は省略するが、作り方は次のファイル・オープン・ピッカーとよく似ている。

ファイル・オープン・ピッカー

 ファイル・オープン・ピッカーの送り側は、前記事で述べた(FileOpenPickerクラスを使う)ので、ここでは省略する。

 このコントラクトに対応しているアプリがあると、次の画像のように、ファイル・オープン・ピッカーのドロップダウンリストに表示される。そこでアプリを選択すると、受け側のOnFileOpenPickerActivatedメソッドが呼び出されることになる。

ファイル・オープン・ピッカー
これはメール・アプリで添付ファイルを選ぶために開いたファイル・オープン・ピッカー。左上の[ファイル]と表示されているドロップダウンを展開してみると、通常のフォルダの下に、ファイル・オープン・ピッカー・コントラクトに対応したアプリがリストされている。

受け側

必要な宣言:ファイル・オープン・ピッカー(必須項目: サポートされるファイルの種類(デフォルトは全ての種類))

 Visual Studioの(メニューバーの)[プロジェクト]−[新しい項目の追加]から表示されるダイアログで、「ファイル オープン ピッカー コントラクト」を選択すると、マニフェスト・ファイルとApp.xaml.csファイルの修正と、画面のひな型を追加してくれる。

 App.xaml.csファイルに追加されるメソッドは、次のとおり。

protected override void OnFileOpenPickerActivated(FileOpenPickerActivatedEventArgs args)
{
  var fileOpenPickerPage = new SimpleNotepad.FileOpenPickerPage();
  fileOpenPickerPage.Activate(args);
}
List 4-1: 自動的に追加されたOnFileOpenPickerActivatedメソッド(App.xaml.cs)

 引数の中のargs.FileOpenPickerUI.AllowedFileTypesプロパティに、送り側が欲しているファイルの拡張子が、文字列のリストとして格納されている。自動生成されたfileOpenPickerPage.Activateメソッドの中に、拡張子に該当するファイルを検索して表示する実装を追加する。

 これで次の画像のように、ほかのアプリが開いたファイル・オープン・ピッカーの中に表示されるようになる。

ファイル・オープン・ピッカー(背景が濃緑色の部分)の中に表示される
ここまでの実装で表示されるようにはなった(背景が黒色の部分)が、ファイルを選択しても右下のOKボタン(画像のメール・アプリでは[添付]ボタン)は無効のままで押すことができない。

 次は、ファイルが選択/選択解除されたときに、そのことをファイル・オープン・ピッカーに伝えねばならない。OnFileOpenPickerActivatedメソッドの引数「args」のFileOpenPickerUIプロパティには、ファイル・オープン・ピッカーのオブジェクトが入っているので、そのオブジェクトをメンバ変数「_fileOpenPickerUI」に保持しておき、次のようにしてファイルの選択と選択解除を通知する。

private void FileGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  // e.RemovedItemsプロパティには、ユーザーが選択解除したファイルが、
  // e.AddedItemsプロパティには、ユーザーが新しく選択したファイルが入っているとする。

  foreach (var item in e.RemovedItems)
    this._fileOpenPickerUI.RemoveFile((item as SampleDataItem).UniqueId);

  foreach (var item in e.AddedItems)
  {
    var dataItem = item as SampleDataItem;
    this._fileOpenPickerUI.AddFile(dataItem.UniqueId, dataItem.FileInfo);
  }
}
List 4-2: 自動的に追加されたFileGridView_SelectionChangedメソッド(中身無し)に、実装を追加する(FileOpenPickerPage.xaml.cs)

 なお、引数として渡しているUniqueIdは文字列ならば何でもよいが、これをキーとして選択解除をするので、重複しないように気を付ける必要がある。ファイル・オープン・ピッカーの場合は、ファイルのフルパスを使うのが簡単だ。

 これで実装は完了である。次の画像のようになる。

選択したファイルがファイル・オープン・ピッカーにも表示される
左下に選択したファイルが表示されている。また、右下のOKボタン(画像のメール・アプリでは[添付]ボタン)も押せる状態になっている。

 この記事で紹介したサンプル・コードの全体は、CodePlexで公開している。

  • CodePlex: MetroContractSample ― ファイル・アクティベーション(送り側)、共有コントラクト(送り側)、ファイル・オープン・ピッカー(受け側)
  • CodePlex: bwNotepad for Metro ― ファイル・アクティベーション(受け側)、共有コントラクト(受け側)

まとめ

 コントラクトとそれを実現するWindowsアクティベーション・プラットフォームによって、ユーザーの安全・安心を損なうことなくアプリを連携させることができる。コントラクトには多くの種類があるが、送り側・受け側の挙動を分けて考えると理解しやすい。

 Metroスタイル・アプリは、コントラクトに対応することを義務付けられてはいないが、対応していないと競争には不利だろう。しっかり理解して、ユーザーに喜んでもらえるアプリを作ってほしい。end of article

【2012/12/05 追記】 本稿で紹介したサンプル・コードをWindows 8製品版とVisual Studio Express製品版に対応させたものを、マイクロソフトのサイト「Windows Store app samples」で公開している。

 

 INDEX
  [特集] 続・Windows 8開発に向けて準備しよう
  「コントラクト」でMetroスタイル・アプリのサンドボックスを乗り越える!
    1.コントラクトとアクティベーション・パターン
  2.コントラクトの実装例


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH