連載
» 2014年05月29日 09時37分 UPDATE

WinRT/Metro TIPS:WindowsとPhoneでリソースを切り分けるには?[ユニバーサルWindowsアプリ開発]

WindowsアプリとWindows Phoneアプリで共通のリソースを共有し、それぞれのアプリに固有のアプリは独自に保有するための方法を解説する。

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

powered by Insider.NET

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

連載目次

 ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するとき、画像ファイルや文字列リソースファイルなどのリソースも共有プロジェクトに置ける。では、WindowsストアアプリとWindows Phoneアプリで異なるリソースはどのように配置したらよいだろうか? 本稿ではその方法を解説する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #76」からダウンロードできる。

事前準備

 ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。

  • SLAT対応のPC*1
  • 2014年4月のアップデート*2適用済みの64bit版Windows 8.1 Pro版以上
  • Visual Studio 2013 Update 2*3適用済みのVisual Studio 2013(以降、VS 2013)*4

*1 SLAT対応ハードウェアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウェアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。

*2 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。

*3 マイクロソフトのダウンロードページから誰でも入手できる。

*4 本稿に掲載した手順を試すだけなら、無償のExpressエディションで構わない。Visual Studio Express 2013 Update 2 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。


用語

 本稿では、紛らわしくない限り次の略称を用いる。

  • Windows:Windows 8.1とWindows RT 8.1(2014年4月のアップデートを適用済みのもの)
  • Phone:Windows Phone 8.1

サンプルコードについて

 Visual Studio 2013 Update 2のRTMがリリースされたが、残念なことに本稿執筆時点ではVB用のユニバーサルプロジェクトのテンプレートがまだ含まれていない。そのため、別途公開しているサンプルコードはC#だけとした。しかし本稿の内容はコードビハインドに立ち入らないので、VB/C#に関係なくご利用いただけることと思う。

概要

 ユニバーサルプロジェクトとは、大ざっぱにいえば共有プロジェクトをマージしてビルドする仕組みだ(次の図)。画像ファイルなどのリソースファイルは、WindowsとPhoneに共通するものは共有プロジェクトに、そうでなければそれぞれのプロジェクトに配置すればよい。ただし、多少異なるものもあるので、本稿では以下の3点に分けて説明していく。

  • 画像/フォント/HTML/音声ファイルなど
  • 文字列リソース(「.resw」ファイル)
  • リソースディクショナリ(「App.xaml」ファイルなどに記述する<Resources>要素)
ビルド時に共有プロジェクトがマージされる ビルド時に共有プロジェクトがマージされる

画像/フォント/HTML/音声ファイルなどを切り分けるには?

 概要で述べたように、適切なプロジェクトに配置すればよい。

 例として、筆者が公開しているアプリ*5のソースコードを見てもらいたい(次の画像)。フォント/HTML/音声ファイルは、WindowsとPhoneで共通に使うため、共有プロジェクトに置いている。画像ファイルは、共通に使うものだけを共有プロジェクトに置き、そうでないものはWindowsとPhoneのプロジェクトに分けて配置している。

リソースファイルの配置例(VS 2013) リソースファイルの配置例(VS 2013)
共有プロジェクトに、フォント/HTML/音声ファイルを置いている。
画像ファイルは、共通に利用するものだけを共有プロジェクトに置き、それ以外はそれぞれのプロジェクトに配置している(この画像では見えないが、さらに上の方にWindows用のプロジェクトがあり、そこにWindows専用の画像ファイルが配置されている)。スタート画面のタイルの画像はWindowsとPhoneで同じ絵にしているのだが、解像度の違い(=ファイル名中の「.scale-XXX.」で識別)によって配置場所を変えている。

*5 ユニバーサルWindowsアプリ「クラウディアさんタイマーVer.2」(Windows用Phone用)。そのソースコードは、Windows Store app samples:クラウディアさんタイマー (Claudia Madobe Timer) Ver.2で公開している。


 スタート画面のタイル画像について、補足しておく。

 画像ファイルの解像度は、96dpiを「スケール100」(=100%)として、他の解像度のものをそれに対するパーセントで表す。WindowsとPhoneでサポートされているタイル画像の解像度と配置場所をまとめると、次の表のようになる。

スケール Windows Phone 配置場所
80% ○オプション ×非サポート Windowsプロジェクト
100% ◎必須 ○オプション 共有プロジェクト
120% ×非サポート ○オプション Phoneプロジェクト
140% ○オプション ○オプション 共有プロジェクト
160% ×非サポート ○オプション Phoneプロジェクト
180% ○オプション ○オプション 共有プロジェクト
200% ×非サポート ○オプション Phoneプロジェクト
220% ×非サポート ○オプション Phoneプロジェクト
240% ×非サポート ◎必須 Phoneプロジェクト
300% ×非サポート ○オプション Phoneプロジェクト
400% ×非サポート ○オプション Phoneプロジェクト
スタート画面のタイルの解像度とその配置場所
Phoneで「」印を付けた解像度は、サポートされているが、マニフェストファイルでは指定できないもの(MSDNの「タイルとトーストの画像サイズ」を参照)。
配置場所で共通プロジェクトとあっても、違う絵にするのならば当然それぞれのプロジェクトに配置する。
ストアにアップロードするパッケージをアプリバンドルにした場合、基本パッケージに含まれるのは「◎必須」の画像だけである。スケール指定した画像ファイル(タイル以外の画像も含む)を配置するときには「◎必須」の解像度のものを忘れないようにしよう(例えばスケール180%の画像だけを提供すると、それはリソースパッケージに入ってしまい、180%をサポートしていない端末にはインストールされない)。

文字列リソースを切り分けるには?

 文字列リソース(「.resw」ファイル)の場合は、少し特殊だ。

 共有プロジェクトとWindows/Phoneの各プロジェクトに、同じファイル名の文字列リソースを配置できるのだ。そして、ビルド時に、これらのファイルの内容をマージしてくれるのである。

 試してみよう。VS 2013でユニバーサルプロジェクトを作り、3つのプロジェクトにそれぞれ「Strings」というフォルダーを作成し、そこに「Resources.resw」という名前で文字列リソースファイルを置く(新しい項目を追加するダイアログで[リソース ファイル (.resw)]を選ぶ。次の画像)。

文字列リソースファイルを同名で各プロジェクトに配置した(VS2013) 文字列リソースファイルを同名で各プロジェクトに配置した(VS2013)
Windows/Phone/共有の各プロジェクトに「Strings」フォルダーを作り、「Resources.resw」ファイルを作成したところ(赤枠内)。
パス名(=「Strings」)/ファイル名ともに同じであるから、単純にファイルレベルで同じ場所にコピーしてマージしようとすれば衝突するはずだ。実際、画像ファイルなどでは衝突してしまい、ビルドエラーになる。しかし、文字列リソースファイルの場合は、ファイルの内容を読み取ってマージしてくれる。リソースファイル内に定義した文字列リソースの個々の名前が衝突していなければ大丈夫なのだ。

 それぞれの「Resources.resw」ファイルには、次の画像のような文字列リソースを定義する。

「Resources.resw」ファイルの定義内容(VS2013)
「Resources.resw」ファイルの定義内容(VS2013)
「Resources.resw」ファイルの定義内容(VS2013) 「Resources.resw」ファイルの定義内容(VS2013)
上から、共有プロジェクトの「Resources.resw」ファイル、Windowsプロジェクトのもの、Phoneプロジェクトのものである。
文字列リソースとして定義している名前と値は、以下の通り。
共有プロジェクト:[CommonString.Text]−[共通の.resw]
Windowsプロジェクト;[IndividualString.Text]−[Windows専用の.resw]
Phoneプロジェクト;[IndividualString.Text]−[Phone専用の.resw]

 上で定義した文字列リソースを、次のようなXAMLコードで利用する。

<Border ……省略…… >
  <TextBlock x:Uid="CommonString" ……省略…… />
</Border>
<Border ……省略…… >
  <TextBlock x:Uid="IndividualString" ……省略…… />
</Border>

XAMLコードから上記の文字列リソースを利用する(XAML)
WindowsとPhondeで同じXAMLコードを書く。
上の「x:Uid="CommonString"」となっているテキストブロックには、共有プロジェクトに置いた「Resources.resw」ファイルに定義されている文字列が実行時に設定される(詳しくは「WinRT/Metro TIPS:文字列リソースを使うには?[Win 8]」で解説している。)。
下の「x:Uid=" IndividualString"」となっている方には、Windows/Phoneそれぞれのプロジェクトに置いてある「Resources.resw」ファイルに定義されている文字列が実行時に設定されるのだ。

 実行結果を本稿の最後に掲載しておく。

リソースディクショナリを切り分けるには?

 スタイル定義やテンプレート定義、あるいは文字列や色などのリソースを記述するリソースディクショナリを切り分けるには、ResourceDictionaryクラス(Windows.UI.Xaml名前空間)のMergedDictionariesプロパティを利用する。リソースディクショナリの中に、別ファイルで定義したリソースディクショナリを取り込むのだ。

 例として、共有プロジェクトの「App.xaml」ファイルに定義してあるリソースディクショナリに、Windows/Phoneプロジェクトに置いたリソースディクショナリを取り込んでみよう。まず、Windows/Phoneプロジェクトに「App.Resources.xaml」というファイル名でリソースディクショナリを追加する(新しい項目を追加するダイアログで[リソース ディクショナリ]を選び、ファイル名を指定する。次の画像)。

リソースディクショナリを追加したところ(VS 2013) リソースディクショナリを追加したところ(VS 2013)
Windows/Phoneプロジェクトにそれぞれ「App.Resources.xaml」というファイル名でリソースディクショナリを追加した(赤枠内)。
ここにリソース定義を記述しただけでは、アプリのリソースとして認識されない。以下に説明するように、このリソースディクショナリのファイルを取り込んで利用する。

 Windowsプロジェクトの「App.Resources.xaml」ファイルには、次のコードのようにWindows専用の定義を書く。

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MetroTips076CS">

  <x:String x:Key="IndividualColorTitle">Windows専用の色定義</x:String>
  <SolidColorBrush x:Key="IndividualColor">Blue</SolidColorBrush>

</ResourceDictionary>

Windowsプロジェクトの「App.Resources.xaml」ファイル(XAML)
「IndividualColorTitle」というキー名で「Windows専用の色定義」という文字列を、また、「IndividualColor」というキー名で青色のSolidColorBrushオブジェクト(Windows.UI.Xaml.Media名前空間)を定義した。

 Phoneプロジェクトの「App.Resources.xaml」ファイルには、次のコードのようにPhone専用の定義を書く。

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MetroTips076CS">
    
  <x:String x:Key="PageTitle01">リソースの切り分け</x:String>

  <x:String x:Key="IndividualColorTitle">Phone専用の色定義</x:String>
  <SolidColorBrush x:Key="IndividualColor">DarkRed</SolidColorBrush>

</ResourceDictionary>

Phoneプロジェクトの「App.Resources.xaml」ファイル(XAML)
「IndividualColorTitle」というキー名で「Phone専用の色定義」という文字列を、また、「IndividualColor」というキー名で濃い赤色のSolidColorBrushオブジェクトを定義した。
さらに、キー名「PageTitle01」として、Phoneプロジェクトに固有の文字列も定義してある。

 そして、共有プロジェクトの「App.xaml」ファイルで、上記の「App.Resources.xaml」ファイルを取り込んで利用する(次のコード)。思い出してほしいのだが、ユニバーサルプロジェクトがビルドされるときに上記2つの「App.Resources.xaml」ファイルのうちどちらかが取り込まれるのである。

<Application
    x:Class="MetroTips076CS.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MetroTips076CS">

  <Application.Resources>
    <ResourceDictionary>
      
      <!-- 共通のリソース -->
      ……省略……
      <x:String x:Key="CommonColorTitle">共通の色定義</x:String>
      <SolidColorBrush x:Key="CommonColor">DarkGreen</SolidColorBrush>
      ……省略……

      <!-- Windows/Phone個別のリソースディクショナリ -->
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="App.Resources.xaml" />
      </ResourceDictionary.MergedDictionaries>

    </ResourceDictionary>
  </Application.Resources>
</Application>

共有プロジェクトの「App.xaml」ファイルに定義したリソースディクショナリ(XAML)
「<ResourceDictionary.MergedDictionaries>」タグを使って、「App.Resources.xaml」ファイルを取り込んでいる。ビルド時にWindows/Phoneどちらかのプロジェクトに定義されている「App.Resources.xaml」ファイルが利用されることを思い出してほしい。
また、取り込むリソースディクショナリとは別に、「CommonColorTitle」というキー名で「共通の色定義」という文字列を、「CommonColor」というキー名で濃い緑色のSolidColorBrushオブジェクトも定義してある。

 このマージされたリソースディクショナリを、次のようにXAMLコードで利用できる。

<Border Background="{StaticResource CommonColor}">
  <TextBlock Text="{StaticResource CommonColorTitle}" />
</Border>
<Border Background="{StaticResource IndividualColor}">
  <TextBlock Text="{StaticResource IndividualColorTitle}" />
</Border>

XAMLコードから上記のリソースディクショナリを利用する(XAML)
WindowsとPhondeで同じXAMLコードを書く。
上のテキストブロックには、共有プロジェクトに置いた「App.xaml」ファイルに定義されているSolidColorBrushオブジェクトと文字列が適用される。
下の方には、Windows/Phoneそれぞれのプロジェクトに置いてある「App.Resources.xaml」ファイルに定義されているSolidColorBrushオブジェクトと文字列が適用されるのだ。

 また、Phoneのプロジェクトでは、「PageTitle01」というキー名の文字列リソースが定義されているので、次のコードのように使える。

<TextBlock Text="{StaticResource PageTitle01}" ……省略…… />

Phoneプロジェクトでは「PageTitle01」という文字列リソースが使える(XAML)
Phoneの一般的な画面レイアウトでは、上部のタイトルが2段になっている(Windowsは1段)。2段目のタイトル文字列は、このようにPhone独自に定義することになる。

実行結果

 以上のうち、文字列リソースファイル(.reswファイル)とリソースディクショナリの切り分け方法を実行してみた結果は、次の画像のようになる。共有プロジェクトに置いたリソースと、Windows/Phoneそれぞれのプロジェクトに置いたリソースがきちんと使われている。

実行しているところ 実行しているところ
左側のWindowsストアアプリは、VS 2013付属のシミュレータでデバッグ実行している。右側のPhoneアプリは、「Windows Phoneの画面出力アプリ」(英語名は「Project My Screen App for Windows Phone」)を使って、実機で実行している画面を表示している。

まとめ

 WindowsとPhoneに共通するリソースは共有プロジェクトに、そうでなければそれぞれのプロジェクトに配置すればよい。ただし、文字列リソースは少し特殊でファイル名が衝突してもよい。また、リソースディクショナリは取り込むためのXAMLコードの記述が必要だ。

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

WinRT/Metro TIPS

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

@IT Special

- PR -

TechTargetジャパン

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

Focus

- PR -

RSSについて

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

メールマガジン登録

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