WindowsストアアプリとPhoneアプリで、細部が異なるだけでほぼ同一のUIを共有したい場合にはどうすればよいだろうか。サードパーティ製のライブラリを用いて、これを実現する方法を解説する。
powered by Insider.NET
ユニバーサルプロジェクトの共有プロジェクトでUIを記述しているときに、XAMLコードを切り分けたいと思ったことはないだろうか? Windowsストアアプリ(以降、Windowsアプリ)とWindows Phone 8.1のWindows Runtimeアプリ(以降、Phoneアプリ)で、ほとんど同じUIなので共有プロジェクトに置きたいのだが、ほんの少しだけコントロールやその属性が異なるような場合だ。例えば、WindowsアプリとPhoneアプリで、1カ所だけコントロールを変えたいようなときだ。あるいは、デバッグビルドとリリースビルドで、コントロールや属性を使い分けたいこともあるだろう。そのようなことは標準ではできないが、サードパーティ製のライブラリを利用することで可能になる。本稿ではその使い方を解説する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #82」からダウンロードできる。
ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Express 2013 for Windowsを使っている。
*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 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。
*4 マイクロソフトのダウンロードページから誰でも入手できる。
*5 本稿に掲載したコードを試すだけなら、無償のExpressエディションで構わない。Visual Studio Express 2013 Update 2 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsストアアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。
本稿では、紛らわしくない限り次の略称を用いる。
Visual Studio 2013 Update 2では、残念なことにVB用のユニバーサルプロジェクトのテンプレートは含まれていない*6。そのため、本稿で紹介するコードはC#のユニバーサルプロジェクトだけとさせていただく*7。
*6 VB用のユニバーサルプロジェクトは、来年にリリースされるといわれているVisual Studio「14」からの提供となるようだ。「Visual Studio UserVoice」(英語)のリクエストに対する、6月18日付けの「Visual Studio team (Product Team, Microsoft)」からの回答による。
*7 Visual Studio 2013 Update 2のVBでユニバーサルWindowsアプリを作る場合のお勧めは、「The Visual Basic Team」のブログ記事(英語)によれば、PCLを使う方法のようである。しかし、本稿で説明する方法は、PCLでは利用できない。
「XAML Conditional Compilation」(以降、XCC)は、「XAML Spy」(WindowsストアアプリやWPFアプリのXAMLを解析するツール)などで有名なFirst Floor Software(オランダ)が無償で公開しているライブラリである。
このライブラリを組み込むことで、共有プロジェクトにおいてXAMLの切り分けが可能になる。切り分ける条件として、ビルド時の条件付きコンパイルシンボルを利用する。そのため、WindowsとPhoneで切り分けることも、デバッグビルドとリリースビルドで切り分けることもできる。切り分けの対象は、コントロールまたはその属性である。なお、Windows/Phoneだけでなく、Xamarin Formsにも対応しているとのことだ。
まず、ユニバーサルプロジェクトのソリューションに対して、NuGetからXCCのパッケージをインストールする。
それには、ソリューションエクスプローラーでソリューションを右クリックし、そのメニューから[ソリューションの NuGet パッケージの管理]を選ぶと[NuGet パッケージの管理]ダイアログが表示される。そこで、左のリストで[オンライン]を選び、右上の検索ボックスに「XCC」と入力してXCCのパッケージを検索する。XCCが見つかったら、その脇の[インストール]ボタンをクリックする(次の画像)。
XCCのインストールを指示すると、[プロジェクトの選択]ダイアログが表示される(次の画像)。ここはデフォルトのまま(=WindowsとPhoneの両方のプロジェクトにインストールする)として、[OK]ボタンをクリックしてインストールを実行する。
XCCがインストールできたら、切り分けを行いたいXAMLファイルの先頭の要素に属性をいくつか追加する。
ここでは、ユーザーコントロールでやってみよう*8。共有プロジェクトに新しくユーザーコントロールを作り、ファイル名は「MyDatePickerControl.xaml」とする。その先頭の要素(=<UserControl>タグ)に、次のコードのように属性を追加する。
<UserControl
x:Class="MetroTips082CS.MyDatePickerControl"
……省略……
d:DesignWidth="400"
xmlns:win81="condition:WINDOWS_APP"
xmlns:wp81="condition:WINDOWS_PHONE_APP"
mc:Ignorable="d win81 wp81"
mc:ProcessContent="win81:* wp81:*"
>
以上でXCCを利用する準備は完了だ。
*8 XCCの現在バージョン(=1.0.2)では、ユーザーコントロールに使用する場合にちょっとした問題がある。もちろん、ビルドや動作に支障はない。そのユーザーコントロールを貼り付けた画面で、デザイン時にそのユーザーコントロールが表示されないのである。将来のバージョンで改善されることを期待したい。
一方のプロジェクトだけで使用したいコントロールに、名前空間プレフィックス「win81」/「wp81」を付ければよい。
例えば、WindowsではDatePickerコントロール(Windows.UI.Xaml.Controls名前空間)、PhoneではDatePickerFlyoutコントロール(Windows.UI.Xaml.Controls名前空間)を使いたい場合には、次のコードのように記述する。
<win81:DatePicker /><!-- ←Windowsプロジェクトをビルドするときに有効 -->
<wp81:DatePickerFlyout /><!-- ←Phoneプロジェクトをビルドするときに有効 -->
一方のプロジェクトだけに適用したい属性に、名前空間プレフィックス「win81」/「wp81」を付ければよい。
例えば、StackPanelコントロール(Windows.UI.Xaml.Controls名前空間)の積み重ねの方向を、Windowsでは横、Phoneでは縦にしたい場合には、次のコードのように記述する。
<StackPanel win81:Orientation="Horizontal" wp81:Orientation="Vertical">
……省略……
</StackPanel>
以上をまとめて、次のコードのようなユーザーコントロールに仕上げてみた。ボタンをタップすると日付を選ぶフライアウトが出てくるのだが、その日付選択にはWindowsではDatePickerコントロールを、PhoneではDatePickerFlyoutコントロールを使用する。また、ボタンと日付を表示するTextBlockコントロール(Windows.UI.Xaml.Controls名前空間)の位置関係を、Windowsでは左右、Phoneでは上下とした。
<UserControl
x:Class="MetroTips082CS.MyDatePickerControl"
……省略……
d:DesignWidth="400"
xmlns:win81="condition:WINDOWS_APP"
xmlns:wp81="condition:WINDOWS_PHONE_APP"
mc:Ignorable="d win81 wp81"
mc:ProcessContent="win81:* wp81:*"
>
<Grid>
<!-- StackPanelコントロール。Windowsでは横方向、Phoneでは縦方向に積み重ねる -->
<StackPanel win81:Orientation="Horizontal" wp81:Orientation="Vertical">
<!-- AppBarButtonコントロール。タップするとフライアウトが表示される -->
<AppBarButton Icon="Calendar" IsCompact="True" VerticalAlignment="Center" >
<!-- Windows用のフライアウトの定義 -->
<win81:AppBarButton.Flyout>
<Flyout Placement="Right">
<DatePicker x:Name="DatePicker1" FontSize="32" />
</Flyout>
</win81:AppBarButton.Flyout>
<!-- Phone用のフライアウトの定義 -->
<wp81:AppBarButton.Flyout>
<wp81:DatePickerFlyout x:Name="DatePicker1" />
</wp81:AppBarButton.Flyout>
</AppBarButton>
<!-- 年月日を表示するTextBlockコントロール。「DatePicker1」にバインドしている -->
<TextBlock Grid.Column="1" FontSize="30" VerticalAlignment="Center" Margin="5,3,0,0" >
<Run Text="{Binding Date.DateTime.Year, ElementName=DatePicker1, Mode=OneWay}" />年
<Run Text="{Binding Date.DateTime.Month, ElementName=DatePicker1, Mode=OneWay}" />月
<Run Text="{Binding Date.DateTime.Day, ElementName=DatePicker1, Mode=OneWay}" />日
</TextBlock>
</StackPanel>
</Grid>
</UserControl>
こうした作ったユーザーコントロールを、WindowsとPhoneの画面に貼りつけてビルドし、実行してみると次の画像のようになる。
XAML Conditional Compilationを利用すると、WindowsとPhoneでXAMLを切り分けられる。一部だけ別のコントロールを使ったり、部分的に属性を異なる値に設定したりできる。WindowsとPhoneでコントロールの配置を微調整することもできるだろう。
XAML Conditional Compilationは次のサイトでソースコードやドキュメントが公開されているので、参考にしてほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.