February 2016

Volume 31 Number 2

Windows 10 - Web 開発者とユニバーサル Windows プラットフォーム アプリ

Tim Kulp | February 2016

企業の Web 開発者は、HTML、CSS、および JavaScript をしっかりと理解しています。このような開発者は、サポート対象の複数のブラウザーやデバイス間で機能を維持しつつ、画面サイズに柔軟に適応するレスポンシブ Web アプリケーションをビルドしています。このように Web を際立たせるスキルは、ユニバーサル Windows プラットフォーム (UWP) アプリをビルドする際にも同じように活用できます。クロスブラウザー対応のアダプティブ Web アプリケーションから習得した知識があれば、デスクトップであれ、モバイルであれ、Windows 10 プラットフォームを対象とする UWP アプリに頭を切り替えるのは簡単です。

今回は、クロスブラウザー アプリケーションで習得した Web 開発の知識を生かして、任意の Windows デバイスで動作する柔軟な UWP アプリを開発する方法を取り上げます。そこで、まず、レスポンシブ インターフェイスを作成するための CSS や HTML に関する基礎知識が、UWP アプリにどのように生かせるかを調べます。次に、特定のデバイス機能に基づいてアプリを構造化するための、ビューステートと XAML ビューについて調べます。最後に、特定のブラウザーをターゲットにする際のシンプルな JavaScript の使い方を基にして、ターゲット デバイスにアダプティブ コードを当てはめます。

Web 開発者が XAML を使用する理由

今回は、Web 開発と XAML の作成を並列に行います。Web 開発者は、既に HTML/CSS/JavaScript についての豊富なスキルを備えているため、このスキルを生かして UWP アプリをビルドしたいと考えているとします。HTML と JavaScript を好んでいる方は、それだけを使用しても問題ありません。UWP が初めてで、どこから手を付けてよいかわからないときは、XAML が優れたツールになります。それは、XAML では厳密な型指定が求められるためです。

XAML と HTML を比較する場合によく取り上げられる例に、グリッドベースのレイアウトがあります。XAML でグリッド レイアウトを作成する場合、まずグリッド コントロールを追加し、列と行を定義してから、各コントロールをグリッド内の指定したセルや行に割り当てます。HTML の場合は以下のようにさまざまな方法があります。

  • float を使って高さと幅を定義したセルを作成し、clear を使って新しい行を開始する
  • 列と行を定義した各子要素を備えたコンテナー要素に display:grid を使用する
  • tr 要素と td 要素を指定して表を作成する

どの方法で実装するかは CSS や HTML の知識次第ですが、いずれにしても、XAML のグリッド コントロールとは違って、IntelliSense のようなツールのサポートを受けることはできません。厳密に型指定される XAML のコントロールでは、IntelliSense によって UI を作成する方法が理解しやすくなり、UWP の初心者には大きな助けになります。

厳密に型指定されるコードは、問題を解決する場合にも非常に役立ちます。HTML/CSS/JavaScript では、開発者に大きな柔軟性が提供され、ある程度自由に型指定できるコードを使用して、アプリのニーズに合わせて規則を変えることができます。これはアプリのビルドには有効ですが、アプリのサポートには向いていません。型指定に自由度のあるコードをトラブルシューティングする場合、型が変化したり、オブジェクトが動的に変化すると、解決が難しくなることがあります。企業の開発者にとってアプリのビルドは楽しいものですが、どこかの時点で、そのアプリの動作をだれかが管理しなければなりません。厳密に型指定されるオブジェクトと IntelliSense のサポートによって、アプリの機能に簡単に移動できるため、サポート チームはアプリを理解するのが容易になります。

HTML/CSS/JavaScript に取り組む情熱があれば、UWP は、既存のコードを使用して優れたアプリを開発できるプラットフォームになります。HTML/CSS/JavaScript も XAML も優れたツールですが、どちらにも多くの長所と短所があります。JavaScript よりも XAML を好む場合 (bit.ly/1NxUxqh、英語) も、XAML よりも JavaScript を好む場合 (bit.ly/1RSLZ2G) もそれぞれ理由があります。Web アプリケーションに関しては HTML が好みですが、UWP 初心者の方には XAML がお勧めです。XALM では、UWP 内のコントロールについて理解できるようになり、厳密に型指定されるコードと IntelliSense の密接な統合によってチームによるサポート コストが削減されて、新しい知識の習得が楽しくなります。

既知の知識を基礎にした作成

UWP の基礎は、Web デザインの基礎と多くの点が似ています。Web 開発における HTML と JavaScript との懸案事項の分離といった基本的な考え方は、UWP に置き換えれば XAML と XAML.cs コード分離ファイルの関係になります。つまり、すべてのロジックは分離コード ファイルに収め、すべてのプレゼンテーションは XAML ファイルで管理します (これはすべてのロジックを JavaScript で記述し、プレゼンテーションを HTML と CSS で表現するのとまったく同じです)。そのうえ、最近の Web アプリケーションの多くは、Knockout や AngularJS などのフレームワークを利用して、モデル - ビュー - ビューモデル (MVVM: Model-View-ViewModel) デザイン パターンによるデータ バインドの実装も行っています。一般的に、これらのフレームワークや MVVM に関する知識は、UWP のデータ バインドを理解する基礎になります。Web 開発と UWP では構文が異なりますが、基本的な概念について言えば、Web 開発者には、複数のデバイス、ブラウザー、機能にまたがるアプリを開発するしっかりとした基礎があることになります。

ここで取り上げる以外にも、状態管理やデータ ストレージなど、Web 開発と UWP には違いがあります。今回は、UI の作成と、アプリが搭載されるデバイスとアプリとの対話が可能かどうかの確認に注目します。

位置指定: RelativePanel に対する Float と Clear

HTML では、ドキュメント オブジェクト モデル内のどこに各要素を配置するかを決定します。HTML は、要素を定義した順序に従って、上から下に各要素がレンダリングされます。CSS が導入されたことで、要素の表示形式 (インライン、ブロックなど)、位置指定 (相対と絶対)、float や clear などの設定に基づいて、要素のレイアウトを洗練できるようになりました。Web 開発者は float を使用することで、上から下に表示されるレンダリングの流れからその HTML 要素を外し、コンテナ要素の左 (float: left) または 右 (float: right) にその要素を配置できます。

ヘッダー、メイン コンテンツ、サイドバー、およびフッターを含むシンプルなレイアウトがあるとします。float を使用して、コンテナーの右端にサイドバーを、コンテナーの左端にメイン コンテンツをレンダリングするようにブラウザーに指示できます。指定した float の値に応じて、要素は他の要素の左側または右側に配置されます。clear は、要素の float 指定を停止し、HTML 標準の上から下への流れに戻します。図 1 は、float を使用してシンプルなレイアウトを作成する例を示しています。

図 1 float を使用したシンプルなレイアウトの作成

div {
  width: 100%;
}
mainContent {
  width: 60%; float: left;
}
  sidebar{
  width: 40%; float: right;
}
clearer {
  clear: both;
}
CSS for design
<header>
</header>
<div>
  <section class="content"></section>
  <section class="sidebar"></section>
  <div class="clearer"></div>
</div>
<footer>
</footer>
HTML for design

Web 開発者がレイアウトを作成するために float と clear を使用する部分に、UWP では RelativePanel というコントロールが提供されます。これは名前の通り、他のコントロールに対して相対でレイアウトを定義できるようにします。RelativePanel は float と同様、コントロールをアンカー コントロールと相対に位置指定する方法を、開発者が管理できるようにします。CSS のクラスも、Web ページ上の要素の位置を決めるために使用します。同じレイアウトを複製するには、コントロール内で RelativePanel と RelativePanel の添付プロパティを使用します。

<RelativePanel>
  <!-- Header is the anchor object for the relative panel -->
  <TextBlock Text="Header" Name="tbHeader"></TextBlock>
  <TextBlock Text="Content" RelativePanel.Below="tbHeader"
    Name="tbContent"></TextBlock>
  <TextBlock Text="SideBar" RelativePanel.RightOf="tbContent"
    RelativePanel.Below="tbHeader" Name="tbSideBar"></TextBlock>
  <TextBlock Text="Footer" RelativePanel.Below="tbSideBar"
    Name="tbFooter"></TextBlock>
</RelativePanel>

上記のコード ブロックでは、各コントロールの位置は、アンカー コントロールとの相対位置です (この場合、アンカーはヘッダーの TextBlock です)。RelativePanel を使用する場合、各コントロールが画面上で他のコントロールから相対で表示されるように位置を割り当てることができます。UWP 開発者は、Web 開発者が float: left を使用するところで RelativePanel.LeftOf を、float: right を使用するところで RelativePanel.RightOf を使用してコンテンツを配置します。ただし、float を使用する部分は似ていますが、通常の位置指定に戻すために clear を使用するという考え方はありません。ただ、前の要素の下に配置されるだけです。CSS に関するスキルが十分でないと float と clear の管理は難しくなる可能性があるのに対し、RelativePanel ではレイアウトに関する問題のトラブルシューティングが簡単になっています。RelativePanel の宣言型の方法では、あるコントロールの位置を他のコントロールとの相対で指定できます。RelativePanel を閉じると、アプリは XAML 通常のレンダリング フロー (HTML のような上から下への形式) に戻ります。

スケーリング: パーセントとピクセル数

サイズの変化にレスポンシブな Web アプリケーションをビルドするには、要素に相対サイズを使用します。ヘッダー、コンテンツ、サイドバー、フッターから構成されるページ レイアウトの UI を、最初はデスクトップ画面用に作成したとします。Web デザイナーが最初に行うのは、このページ レイアウトに最適なピクセル幅を特定することです。この例では、ページ幅が 1000 ピクセルになるとします。デザイン時は、コンテナーのピクセル幅 1000 ピクセルを念頭において各要素を作成します。HTML では、コンテンツ セクションの幅を 800 ピクセルにし、サイドバー セクションの幅を 200 ピクセルにします。「ターゲット / コンテキスト = パーセント」という数式を使用すると、コンテンツ セクションはコンテキスト (この場合のコンテキストはページ幅の 1000 ピクセル) の 80% になり、サイドバーセクションはコンテキストの 20% になります。

Web デザインでパーセントを使用すると、コンテナー サイズの変化に合わせてレイアウトのサイズを変更できます。上記の例では、ユーザーが 1000 ピクセルのページ オブジェクトのサイズを 659 ピクセルに変更すると、コンテンツとサイドバーのサイズがそれぞれ 527 ピクセルと 131 ピクセルに変化します。同様に、特定のポイント サイズやピクセル サイズの代わりに em を使用してフォントのスタイルを作成すると、コンテキストに応じてフォントをスケーリングできます。このように、ウィンドウのサイズとは無関係なデザインにするには、サイズを比率で指定するようにします。

パーセントを使用すれば単純な算術計算になるようにに思えますが、デバイスのピクセル密度やデバイスの向きなど、要素のスケーリング方法に関わる要因が他にもいくつか存在し、予測不可能な要素がデザインに加わります。UWP では、すべての測定値に「有効ピクセル」という考え方を使用してスケーリングを単純にしています。有効ピクセルは、1 ピクセルと同じではありません。有効ピクセルは、UWP スケーリング アルゴリズムを使用して、ユーザーとデバイスの標準距離とピクセル密度を基に、1 有効ピクセルを表現する方法を認識します。

たとえば、Surface Hub はタブレットやスマートフォンよりもはるかに高いピクセル密度を備えています。UWP 開発者に必要なのは Blend などのツールを使って有効ピクセルで作成することだけです。適切な縮小や拡大に必要な複雑な計算は、スケーリング アルゴリズムが処理します。ポイントは 1 つだけで、有効ピクセルは 4 の倍数にしなければなりません。スケーリング アルゴリズムのしくみにより、4 の倍数にすることで、UI がスケーリングされる際にエッジが明確になります。

ビューステートとメディア クエリ

Web 開発では、CSS がアプリケーションのレイアウト情報を提供します。パーセントを使用して作成すると、アプリのサイズを変更できますが、表示の変化状況によっては、デザインの分割や再構成が必要になります。タブレットなどのモバイル デバイス向けのレイアウト作成と、Surface Hub のような 80 インチ超のデバイス向けのレイアウト作成は同じではありません。Web デザインに関する古いたとえに、ユーザーが画面で目にしたとおりのデザインで印刷するニーズがあります。CSS を使えば、デザイナーは CSS メディア クエリを使って画面印刷の問題を解決できます。ユーザーが画面デザインを印刷する際、ブラウザーは画面用の CSS ではなく、印刷用 CSS を使用します。レスポンシブ Web デザインが進化するにつれ、メディア クエリも詳細な情報をサポートできるように拡大されています。以下は CSS メディア クエリの例です。

<link type="text/css" rel="stylesheet" 
  href="styles/719style.css"
  media="screen and (max-device-width: 719px)"/>

このクエリでは、Web アプリケーションを表示するメディアのデバイス画面幅が 719 ピクセル以下の場合、719style.css ファイルを適用します。たとえば、このメディア クエリを使用して float 値をクリアし、コンテンツ要素とサイドバー要素を重ねて表示する場合と並べて表示する場合を切り替えることができます。Web 開発者はメディア クエリを使用して、画面サイズ、解像度、向きなどの多くのオプションに基づいて表示をカスタマイズしています。CSS3 の完全なリストについては、bit.ly/1riUA2h (英語) を参照してください。

UWP では、ViewStateManager をメディア クエリとして使用し、定義したパラメーターに基づいてアプリケーションのデザインを変更できます。VisualStateManager は、複数のビューステートのコンテナー オブジェクトである VisualStateGroups を 1 つ以上含みます。各ビューステートは、セッター (コントロールごとに更新できるプロパティ) とトリガー (セッターを変更するもの) を保持します。ViewStateManager はトリガーを管理して、ビューステートの特定のセッター値を適用するタイミングを把握します。CSS と照らし合わせてみると、トリガーはメディア クエリに近く、セッターは、メディア クエリで参照されるスタイルシート内の style 値に相当します。ここで、図 2 の例を見てください。

図 2 ビューステートのトリガーに基づくントロールの再配置

<VisualStateManager.VisualStateGroups>
  <VisualStateGroup x:Name="ResponseStateGroup">
    <VisualState x:Name="LessThan720">
      <VisualState.Setters>
      <Setter Target="tbSideBar.(RelativePanel.Below)" Value="tbContent"/>
        <Setter Target="tbSideBar.(RelativePanel.RightOf)" Value=""/>
      </VisualState.Setters>
      <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="1"/>
      </VisualState.StateTriggers>
    </VisualState>
    <VisualState x:Name="GreaterThan720">
      <VisualState.Setters>
        <Setter Target="tbSideBar.(RelativePanel.Below)" Value="tbHeader"/>
        <Setter Target=" tbSideBar.(RelativePanel.RightOf)" Value="tbContent"/>
      </VisualState.Setters>
      <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="720"/>
      </VisualState.StateTriggers>
    </VisualState>
  </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

上記のコードでは、2 つのビューステートをセットアップしています。ビューステート LessThan720 は、ウィンドウ幅が 1 ~ 719 ピクセルの場合にトリガーされます。ウィンドウ幅が 720 ピクセルを超えると、ビューステート GreaterThan720 がトリガーされます。各ビューステートは、tbSideBar コントロールの RelativePanel 設定を操作しています。ウィンドウのサイズが 720 ピクセル未満ならば、画面サイズが小さく、コンテンツとサイドバーを並べて表示できません。この場合は、ビューステート LessThan720 がトリガーされ、メイン コンテンツの TextBlock の下にサイドバーを重ねて表示します。これは、LessThan719.css ファイルで float: none に設定するメディア クエリに似ています。

ビューステートはメディア クエリと同様、トリガーに基づいてコントロールの配置やデザインをやり直すために使用できます。インターフェイスが複雑になるにつれ、ビューステートや ViewStateGroup の管理が複雑になる可能性があります。複雑なビューステートの変化の管理は、Visual Studio 2015 の Blend で行うのが最も簡単です。Blend のエディターを使用すれば、新しいビューステートを作成し、アプリのデザイン時にその変化を確認できます。Blend がすべての XAML を作成し、ビューステートの変化をトリガーするのに必要なデータを提供します。Microsoft Virtual Academy では、Blend を使用したビューステート管理について、理解しやすいチュートリアルのビデオを bit.ly/1P94e32 (英語) で配信しています。

ビューによるエクスペリエンスの再設計

モバイル サイトの UI は、ユース ケースの削減や、モバイル サイトで重点を置くテーマの変化により、デスクトップのエクスペリエンスとはまったく異なる場合があります。このようなシナリオでは、Web 開発者は、モバイル エクスペリエンスの効率を上げるためにコンテンツを調整したり、モバイル デバイスの機能に注目することになります。Web 開発者はさまざまな検出手法を駆使して、ユーザーのデバイス向けに調整して再設計したエクスペリエンス (一般には m.webapp.com のようなサイト) に、ユーザーをリダイレクトできます。

UWP ではビューを使って同等の機能を実現します。アプリによっては、多様な機種のデバイスで動作させると、ViewStateManager がツールとして適切でないほど UI の違いが大きくなる場合があります。ビューにより、開発者は新しい XAML UI に、既存のバックエンド コードを利用できるようになります。適切な構造のビューモデルによってビューの使用が容易になり、複数のビューから 1 つのビューモデル オブジェクトを利用できます。Knockout や AngularJS の知識があれば、UWP で適切なビューモデルを作成して、特定の種類のデバイス向けに調整したユーザー エクスペリエンスを提供することができます。

特定のデバイス向けにビューをセットアップするには、アプリの Views フォルダー内に新しいフォルダーを作成します。

Views フォルダーに、DeviceFamily-Mobile という新しいフォルダーを作成します。その結果、モバイル デバイス ファミリ (スマートフォンなど) に含まれるデバイスで MainPage がで要求されたときに、DeviceFamily-Mobile フォルダーにある MainPage.xaml のビューを使用するように、最新のリソース テクノロジに指示されます。他のデバイス ファミリから MainPage が要求された場合、応答は標準の MainPage になります。UWP 開発者はこの機能を使用して、特定の Windows デバイス ファミリにしか求められないユース ケースをターゲットにした UI を作成できます。

アダプティブ コード

Web アプリケーションをビルドする場合、すべてのブラウザーが同じであるわけではありません。企業内には社内標準があっても、社外ユーザー向けの開発では、OS やブラウザーの種類、バージョンの違いにより対応が複雑になります。Web 開発者は、こうした違いを鮮やかに解決する切り札を数多く所持しています。Modernizr (modernizr.com、英語) などのライブラリによって、こうした複雑な部分の処理が容易になるとはいえ、デバイスやブラウザーに応じて機能を有効にするのは、Web 開発者にとって新しいことではありません。

UWP アプリのビルドには、クロスブラウザー機能と同等の複雑さがあります。ユーザーがメモに画像を追加できる、メモ書きアプリを考えてみます。このアプリでは、スマートフォン組み込みのカメラ機能を使った写真撮影を可能にし、スマートフォンに既に存在する画像をユーザーに表示できるようにします。

デバイス固有の機能を使用する場合、まず、適切な拡張 API をプロジェクトに含めます。たとえば、メモ書きアプリの場合、スマートフォンのハードウェア ボタンにアクセスできるようにする必要があります。このボタンを使用するには、Windows Mobile Extensions for the UWP への参照を追加します。そのためには、[プロジェクト]、[参照の追加]、[ユニバーサル Windows]、[拡張機能] の順で選択します。デスクトップ、モバイル、チーム (Surface Hub 用) など、可能な拡張先の一覧が表示されます。プロジェクトに追加する必要のある拡張先を選択し、[OK] をクリックします。

JavaScript ではナビゲーターによる一連のチェックでブラウザーの機能を検出しますが、UWP ではアプリから IsTypePresent メソッドを呼び出して、必要な API が存在するかどうかをチェックします。メモ書きアプリでは、カメラを使用するためのハードウェア ボタンをチェックするために、以下のコードを使用します。

string apiName = "Windows.Phone.UI.Input.HardwareButtons";
if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent(apiName))
{
  Windows.Phone.UI.Input.HardwareButtons.CameraPressed +=
    HardwareButtons_CameraPressed;
}

上記の短いコードにより、アプリは拡張 API を通じて追加された特定のデバイス機能をターゲットにすることができるようになります。CameraPressed イベント ハンドラーの宣言を IsTypePresent メソッドにラップして、API が存在しない場合にイベント ハンドラーが登録されないようにします。API チェックをすることで、API が存在しない場合にアプリがクラッシュしないようにするツールがあります。PlatformSpecific は優れた NuGet パッケージで、ApiInformation.IsTypePresent メソッドを通じて最初に検証されていない拡張 API への参照の特定とラップを単純化します。この NuGet パッケージの詳細については、GitHub のPlatformSpecific ページ (bit.ly/1GvhkF0、英語) を参照してください。

Web 開発とまったく同じように、クライアントや社内標準により、ブラウザーの特定のバージョンをターゲットにする必要がある場合があります。このような状況では、Web 開発者は、インターネットの他の部分では使用していない、特殊なブラウザー構成に注目する必要があります。

同様に、UWP 開発者は既存のコードを維持するために、拡張 API の特定のコントラクトをターゲットにしなければならないことがあります。これは、IT 運用チームが従業員にコンピューターにアップデートを展開するために、高速ループと低速ループを管理しているような企業アプリケーションで非常に役立ちます。高速ループでは、アプリに即座に実装しなければならない拡張 API の優れた新機能を利用できるようにします。低速ループのユーザーは、現在の機能をそのまま使用します。このような状況では、UWP で IsApiContractPresent を使用して、コードの実行前に、拡張 API と必要な特定のバージョンが利用可能かどうかをチェックすることができます。

if(Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent(apiName, 3))
{
  newKillerFunction();
}

上記のコード セグメントでは、指定された apiName がバージョン 3 の場合、アプリは newKillerFunction のみを実行します。バージョンが 3 未満であれば、newKillerFunction は実行されません。後続のバージョン (バージョン 4 など) が存在する場合は、newKillerFunction が実行されます。

まとめ

UWP 開発には、Web 開発者がクロスブラウザー対応のレスポンシブ Web アプリケーションを開発する際に使用していたスキルと知識の多くを活用できます。多種多様な Web ブラウザーを扱ってきた Web 開発者にとっては、レイアウトのデザイン、表示の違いへの対応 (静的と動的の両方)、システム機能へのアクセスは、すべてありふれた作業です。これらのスキルを UWP 開発に当てはめれば、画面サイズ、デバイス、機能に適応する、リッチな UX をビルドできます。


Tim Kulp は、メリーランド州ボルチモア在住のシニア テクニカル アーキテクトです。彼は Web、モバイル、および UWP の開発者、著者、絵描き、父親を兼務し、「熱狂的な科学者を生み出したい」と願っています。Twitter は @seccode (英語) から、LinkedIn は linkedin.com/in/timkulp (英語) からフォローできます。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Kevin Hill に心より感謝いたします。