連載
» 2014年03月27日 15時54分 公開

WinRT/Metro TIPS:共有でファイルを受け取るには?[Windows 8/Windows 8.1ストアアプリ開発]

Windows 8.1ストアアプリ向けに共有ターゲットを実装する方法を説明。また、ファイルを受け取る場合の問題点とその解決策を紹介する。

[山本康彦(http://www.bluewatersoft.jp/),BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 共有ターゲットとしてWindowsストアアプリを作ると、共有コントラクトによって他のアプリからさまざまなデータを受け取れるようになる。共有ターゲットを実装する方法は、基本的にはMSDNのドキュメント「共有コンテンツの受信」に従えばよい。ただし、受け取るデータがファイルの場合、Windows 8.1(以降、Win 8.1)ではちょっとしたコツが必要だ。本稿では、共有ターゲットの実装方法を簡単に説明した後、Win 8.1でファイルを受け取る場合の問題点とその解決策を紹介する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #68(Windows 8版Windows 8.1版)」からダウンロードできる。

事前準備

 Windows 8(以降、Win 8)用のWindowsストアアプリを開発するには、Win 8.1またはWin 8とVisual Studio 2012(以降、VS 2012)が必要である。本稿では64bit版Windows 8 Pro(日本語版)とVisual Studio Express 2012 for Windows 8(日本語版)*1を使用している。

 Win 8.1用のWindowsストアアプリを開発するには、Win 8.1とVisual Studio 2013(以降、VS 2013)が必要である。本稿ではOracle VM VirtualBox上で64bit版Windows 8.1 Pro(日本語版)とVisual Studio Express 2013 for Windows(日本語版)*2を使用している。

*1 マイクロソフト公式ダウンロードセンターの「Microsoft Visual Studio Express 2012 for Windows 8」から無償で入手できる。

*2 マイクロソフト公式サイトの「Microsoft Visual Studio Express 2013 for Windows」から無償で入手できる。


共有コントラクトとは?

 共有コントラクトとは、Windowsストアアプリ間のデータ交換をサポートする仕組みである。詳しくは次の記事を参照してほしい。

*3 このドキュメントに出てくる「タップして送信」(Tap and Send)とは、NFC(=近距離無線通信、近距離通信)やWi-Fi Directによる近接通信(proximity)を使って2台のコンピューターを接続してデータを交換すること。本稿で扱う共有コントラクトとは別の仕組みである。詳細はMSDNの「近接通信とタップ」(Proximity and tapping)を参照。


 エンドユーザーが共有チャームでデータを受け取るアプリ(=共有ターゲット)を指定すると、共有ターゲットがフライアウトとして右端から出てくる。幾つかの例を次の画像に示す(いずれもWin 8.1)。これらの例のフライアウトの右上にある[送信]ボタン(「メール」アプリではアイコンになっている)をタップすると、共有ターゲットで実際にデータが処理される。

「Microsoft Solitaire Collection」から「メール」へ 「Microsoft Solitaire Collection」から「メール」へ
渡されるデータはTextとBitmap(データの種類については後述)。 Solitaire側の[シェアする]ボタンに注目。MSDNのガイドラインでは「共有チャームを開くUIを設置するのは望ましくない」とされているが、この[シェアする]ボタンで共有チャームが開く。

「Internet Explorer」から「リーディングリスト」へ 「Internet Explorer」から「リーディングリスト」へ
渡されるデータはWebLink(Uri)とHTML。 ただし、HTMLといってもページ全体ではなく、フライアウトの上部に表示されている記事タイトル/ロゴ/要約を記述したものである。

「フード&レシピ」から「OneNote」へ 「フード&レシピ」から「OneNote」へ
渡されるデータはApplicationLink(Uri)/HTML/Text。フライアウトにはHTMLフォーマットで渡されたレシピが表示されている。

「フォト」から「メール」へ 「フォト」から「メール」へ
渡されるデータはStorageItems(複数のファイル)。フライアウトには、4つの画像ファイルが表示されている。 本稿では、この「メール」のように複数のファイルを受け取れる共有ターゲットの実装を説明する。

 なお、本稿で扱うのは、共有ターゲット(=データを受け取る側)である。共有ソース(=データを送り出す側)の実装方法については、「連載:Windowsストア・アプリ開発入門:第7回 ほかのアプリにデータを送る」をご覧いただきたい。

共有ターゲットを実装するには?

 プロジェクトに[共有ターゲット コントラクト]テンプレートを追加すればよい。その手順の概要を次からの画像に示す(Win 8/Win 8.1共通)。


Commonフォルダーにファイルが自動的に追加される Commonフォルダーにファイルが自動的に追加される
共有ターゲットの実装に必要なファイルがプロジェクトのCommonフォルダーに不足している場合は、[追加]ボタンをクリックするとこの画像のようにメッセージボックスが表示される。[はい]ボタンをクリック。


変更されたマニフェスト 変更されたマニフェスト
テンプレートが追加されたら、マニフェスト(「Package.appxmanifesst」ファイル)を確認してほしい。[宣言]タブに[共有ターゲット]の宣言が追加されており、そのプロパティのデータ形式のところには[text]と[uri]が設定されている。この[データ形式]とその下の[サポートされるファイルの種類]は、必要に応じて修正することになる。

 以上で、このアプリは共有ターゲットとなり、「text」形式と「uri」形式の共有データを受け取れるようになった。textやuriを共有データとして送り出すアプリ(前述の例では「フォト」を除く3アプリ)を使用中にエンドユーザーが共有チャームを開くと、このアプリも共有ターゲットの1つとして列挙される。そこでエンドユーザーがこのアプリを選ぶと、テンプレートで追加された「ShareTargetPage1.xaml」ファイルがフライアウトとして表示され、そのコードビハインドのActivateメソッドが呼び出される。また、エンドユーザーがフライアウトの[Share]ボタン(前述の例では[送信]ボタンに相当)をタップすると、コードビハインドのShareButton_Clickメソッドが呼び出される。これら2つのメソッドは仮のものなので、必要な実装に修正すればよい。

 実装の指針としては、次のようにするとよいだろう。Activateメソッドではデータを全て受け取って、必要ならば自分が扱えるデータかチェックをし、フライアウト上にプレビューを表示するとともに、データをメンバー変数に格納する。ShareButton_Clickメソッドでは、受け取ったデータ(すでにメンバー変数に格納されている)を処理するだけにする。もしもShareButton_Clickメソッドでデータのチェックを行ってしまうと、エンドユーザーが[Share]ボタンをタップした後でエラーが出ることになってしまい、エンドユーザーをがっかりさせることになる。

共有されるデータの構造

 共有ターゲットが受け取るデータは、DataPackageViewクラス(Windows.ApplicationModel.DataTransfer名前空間)のオブジェクトである。DataPackageViewオブジェクトには、次の図のようにプロパティ(=Propertiesプロパティ)と実データが格納されている。

DataPackageViewオブジェクトの構造 DataPackageViewオブジェクトの構造
共有する実際のデータ(=実データ)は、直接アクセスできない形で格納されている。DataPackageViewクラスには、フォーマットごとに実データを取り出すためのメソッドが用意されている。また、Propertiesプロパティには、実データに付随するプロパティ(データのタイトルや説明など)がセットされている。

 ここで、実データの種類としては、次の表に示すようなものが用意されている。

プロパティ(Win 8) プロパティ(Win 8.1) 型と説明
Bitmap ←(同左) RandomAccessStreamReference
ビットマップイメージ
Html ←(同左) string
HTMLテキスト
Rtf ←(同左) string
書式付きテキスト(RTF)
StorageItems ←(同左) IReadOnlyList>Windows.Storage.IStorageItem<
ファイルとフォルダーのリスト。この表の他のものは全て1つのオブジェクトしか渡せないが、このStorageItemsプロパティだけは複数のオブジェクトを渡せる
Text ←(同左) string
プレーンテキスト
Uri ApplicationLink

WebLink
Uri
WebLinkプロパティはWebで使われるhttpやhttpsプロトコルなど。ApplicationLinkプロパティは、アプリを起動するためのプロトコル(ms-windows-storeプロトコルなど)。
Win 8では両者を区別せず、どちらもUriプロパティとして扱う(Win 8.1では警告は出るがUriプロパティも利用できる)
実データの主な種類
StandardDataFormatsクラス(Windows.ApplicationModel.DataTransfer名前空間)に定義されているものだけを示す。StorageItemsプロパティだけが複雑な型をしている。

テキストやUriなどを受け取るには?

 実データが入っていることを確かめ、それぞれのフォーマットに応じたメソッドを使って取り出せばよい。

 前述した[共有ターゲット コントラクト]テンプレートの追加により、マニフェストにはTextとUriが自動的に追加されている。あとは、Activateメソッドに次に示すコードを記述するだけである。

ファイルを受け取るには?

 主な実データのうちでStorageItemsだけが複雑な型をしている。複数のファイルやフォルダーを受け渡しできる汎用的なものなのだが、その代わりに扱いがちょっと厄介だ。ここでは、「.png」と「.jpg」の2種類の画像ファイルだけを受け取れるようにする方法を解説する。

 まず、共有ターゲットとして「.png」と「.jpg」のファイルを受け取ることを、マニフェストに追記する(次の画像)。

マニフェストに「.png」と「.jpg」のファイルを受け取ることを追記する マニフェストに「.png」と「.jpg」のファイルを受け取ることを追記する
マニフェストの[宣言]タブの[共有ターゲット]の宣言を選び、[サポートされるファイルの種類]の下にある[新規追加]ボタンをクリックすると現れる[ファイルの種類]欄に「.jpg」と入力。その後、もう一度[新規追加]ボタンをクリックして、今度は「.png」と入力。

 次に、Activateメソッドの中でファイルを取り出す。GetStorageItemsAsyncメソッドでIStorageItemのオブジェクト(Windows.Storage名前空間)のリストが得られるので、IStorageItemのオブジェクトをStorageFileクラス(Windows.Storage名前空間)にキャストすればよい。ここでは、渡されたリストに含まれている最初のファイルの画像を自動生成されたコードのthumbnailImage変数(=BitmapImageオブジェクト)にセットしてみよう(次のコード)。

public async void Activate(ShareTargetActivatedEventArgs args)
{
  ……省略……

  // データ形式:StorageItems
  // 注:マニフェストで、受け取れるファイルの種類を制限している(「.png」形式と「.jpg」形式のみ)
  if (dataPackage.Contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.StorageItems)) 
  {
    // GetStorageItemsAsyncメソッドでIStorageItem型のリストが取得できる
    IReadOnlyList<Windows.Storage.IStorageItem> items = await dataPackage.GetStorageItemsAsync();

    // 受け取ったものは「.png」形式か「.jpg」形式のファイルだけのはずなので、StorageFile型にキャストできる
    IEnumerable<Windows.Storage.StorageFile> files = items.Cast<Windows.Storage.StorageFile>();

    // 最初のファイルの画像をthumbnailImage変数にセットする
    using (var stream = await files.First().OpenReadAsync())
    {
      await thumbnailImage.SetSourceAsync(stream);
      this.DefaultViewModel["ShowImage"] = true;
    }
  }
}

Public Async Sub Activate(args As ShareTargetActivatedEventArgs)

  ……省略……

  ' データ形式:StorageItems
  ' 注:マニフェストで、受け取れるファイルの種類を制限している(「.png」形式と「.jpg」形式のみ)
  If (dataPackage.Contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.StorageItems)) Then
    ' GetStorageItemsAsyncメソッドでIStorageItem型のリストが取得できる
    Dim items As IReadOnlyList(Of Windows.Storage.IStorageItem) = Await dataPackage.GetStorageItemsAsync()

    ' 受け取ったものは「.png」形式か「.jpg」形式のファイルだけのはずなので、StorageFile型にキャストできる
    Dim files As IEnumerable(Of Windows.Storage.StorageFile) = items.Cast(Of Windows.Storage.StorageFile)()

    ' 最初のファイルの画像をthumbnailImage変数にセットする
    Using stream = Await files.First().OpenReadAsync()
      Await thumbnailImage.SetSourceAsync(stream)
      Me.DefaultViewModel("ShowImage") = True
    End Using
  End If
End Sub

StorageItemsから画像ファイルを取り出すコード(Win 8用)(上:C#、下:VB)
太字の部分を追加する。
Containsメソッドで目的のデータが入っていることを確かめるのは同じだが、GetStorageItemsAsyncメソッドで得られるものはIStorageItemオブジェクトのリストである。StorageFileクラスにキャストした上で、OpenReadAsyncメソッドでストリームを開き、それをthumbnailImage変数(=BitmapImageオブジェクト)に渡す。
ただし、このコードはWin 8用のWindowsストアアプリ(=VS 2012で作ったWindowsストアアプリ)だけで動作する(また、それをWin 8.1上で実行しても問題無い)。Win 8.1用のWindowsストアアプリではうまくいかない(次で説明する)。

 上のコードをWin 8.1用のWindowsストアアプリで使う(=VS 2013で作る)と、OpenReadAsyncメソッドを呼び出しているところで例外が発生する。Win 8.1用のWindowsストアアプリの実行環境では、次のコードのようにして、OpenReadAsyncメソッドを呼び出してストリームを取得する処理の全体を別スレッドで動かす必要があるようだ。

//using (var stream = await files.First().OpenReadAsync())
// ↓VS2013(Win8.1用Windowsストアアプリ)では、次のようにしないと例外が出る
using (var stream = await System.Threading.Tasks.Task.Run(async () =>
                            await files.First().OpenReadAsync()))

'Using stream = Await files.First().OpenReadAsync()
' ↓VS2013(Win8.1用Windowsストアアプリ)では、次のようにしないと例外が出る
Using stream _
  = Await System.Threading.Tasks.Task.Run(Async Function() Await files.First().OpenReadAsync())

StorageItemsから画像ファイルを取り出すコード(Win 8.1用)(上:C#、下:VB)
変更箇所のみを示す。

実装例

 上で説明したコードを応用して、共有フライアウトにTextとUri、および受け取った画像ファイルの全てを表示するようにしてみた(次の画像)。このソースコードを別途サンプルコードとして公開しているので、興味のある方はぜひそちらもご覧いただきたい。

別途公開しているサンプルコードの実行例 別途公開しているサンプルコードの実行例
ただし、[Share]ボタンの実装はしていないので、共有データを受け取ってフライアウトに表示する以外は何もしない。 なお、画面左側の「共有ソースのサンプル」アプリも、別途公開しているサンプルコードに入っている(VS 2012用のC#のみ)。

まとめ

 共有ターゲット側でファイルやフォルダーを受け取るには、DataPackageViewクラスのContainsメソッドを使って実データにStoregeItemsデータが入っていることを確かめ、同クラスのGetStorageItemsAsyncメソッドでIStorageItemオブジェクトのリストを取り出し、必要に応じてStorageFileクラスやStorageFolderクラスにキャストする。ただし、StorageFileオブジェクトのOpenReadAsyncメソッドを使ってストリームを得るときに、Win 8.1用のWindowsストアアプリ(VS 2013で作成するWindowsストアアプリ)では、ストリームを取り出す処理の全体を別スレッドで行わなければならない。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

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

メールマガジン登録

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