Windows 8.1ストアアプリ向けに共有ターゲットを実装する方法を説明。また、ファイルを受け取る場合の問題点とその解決策を紹介する。
powered by Insider.NET
共有ターゲットとして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)。これらの例のフライアウトの右上にある[送信]ボタン(「メール」アプリではアイコンになっている)をタップすると、共有ターゲットで実際にデータが処理される。
なお、本稿で扱うのは、共有ターゲット(=データを受け取る側)である。共有ソース(=データを送り出す側)の実装方法については、「連載:Windowsストア・アプリ開発入門:第7回 ほかのアプリにデータを送る」をご覧いただきたい。
プロジェクトに[共有ターゲット コントラクト]テンプレートを追加すればよい。その手順の概要を次からの画像に示す(Win 8/Win 8.1共通)。
以上で、このアプリは共有ターゲットとなり、「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プロパティ)と実データが格納されている。
ここで、実データの種類としては、次の表に示すようなものが用意されている。
プロパティ(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プロパティも利用できる) |
実データが入っていることを確かめ、それぞれのフォーマットに応じたメソッドを使って取り出せばよい。
前述した[共有ターゲット コントラクト]テンプレートの追加により、マニフェストにはTextとUriが自動的に追加されている。あとは、Activateメソッドに次に示すコードを記述するだけである。
主な実データのうちでStorageItemsだけが複雑な型をしている。複数のファイルやフォルダーを受け渡しできる汎用的なものなのだが、その代わりに扱いがちょっと厄介だ。ここでは、「.png」と「.jpg」の2種類の画像ファイルだけを受け取れるようにする方法を解説する。
まず、共有ターゲットとして「.png」と「.jpg」のファイルを受け取ることを、マニフェストに追記する(次の画像)。
次に、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
上のコードを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())
上で説明したコードを応用して、共有フライアウトにTextとUri、および受け取った画像ファイルの全てを表示するようにしてみた(次の画像)。このソースコードを別途サンプルコードとして公開しているので、興味のある方はぜひそちらもご覧いただきたい。
共有ターゲット側でファイルやフォルダーを受け取るには、DataPackageViewクラスのContainsメソッドを使って実データにStoregeItemsデータが入っていることを確かめ、同クラスのGetStorageItemsAsyncメソッドでIStorageItemオブジェクトのリストを取り出し、必要に応じてStorageFileクラスやStorageFolderクラスにキャストする。ただし、StorageFileオブジェクトのOpenReadAsyncメソッドを使ってストリームを得るときに、Win 8.1用のWindowsストアアプリ(VS 2013で作成するWindowsストアアプリ)では、ストリームを取り出す処理の全体を別スレッドで行わなければならない。
Copyright© Digital Advantage Corp. All Rights Reserved.