本記事はUWP(Universal Windows Platform)の入門記事第5回です。
今回は、第3回で作成したHello Worldアプリケーションを使って、UWPアプリケーションの基本ファイル構成や、アプリケーションが動作する仕組みについて紹介します。
本記事では以下のことが学習できます。
・UWPプロジェクトの基本ファイル構成
・UWPアプリケーションの基本的な仕組み
本記事は以下の環境を前提としてます。
・Windows 10 OS(Pro以上)
・Visual Studio 2015(+UWP SDK)
プロジェクトがどのようなファイル構成になっているかはVisual Studioの右側(デフォルトのレイアウトの場合)にあるソリューションエクスプローラーで確認できます。
UWPアプリケーション開発を含め、Visual Studioでは開発に必要なプロジェクトを複数まとめたものをソリューションとして扱います。
上記の画像の「ソリューション’Hello_UWP'(1プロジェクト)」というのがHello_Worldというソリューションに一つのプロジェクトが含まれていることを意味します。
その下の「Hello_UWP(Universal Windows)」が、これから解説していくUWPアプリケーションのプロジェクトです。
・Properties
Propertiesには「AssemblyInfo.cs」と「Default.rd.xml」という2つのファイルが配置されています。
AssemblyInfo.csにはアプリケーションのアプリケーション情報を記述できますが、入門編では触ることが無いので、詳しくは解説しません。
Default.rd.xmlには.NETネイティブな指定を記述します。こちらもAssemblyInfo.cs同様に基本的に入門時に触ることが無いファイルです。
・参照
プロジェクトで利用するコンポーネントが表示されています。
デフォルトの状態では基本的なUWPアプリケーションを作成するためのコンポーネントが追加されていますが、ここにデバイス固有の拡張機能や、別のプロジェクトやライブラリを追加することも可能です。
「Microsoft.NETCore.UniversalWindowsPlatform」はUWP用の.NET Coreコンポーネントのことで、左のアイコンはこれらコンポーネントをNuGetというパッケージ管理システムから利用することを示しています。
「Universal Windows」は上記以外のUWPで用いるコンポーネントを追加します。
参照については初期状態では「開発について基本的な機能が利用できる」ようになっていて、それ以外に必要が出たら追加できる場所と慣れないうちは覚えておきましょう。
・Assets
Assetsは画像やメディアファイルなどのファイルを格納しておく場所です。
デフォルトではアイコン画像などが配置されいます。
・App.xaml(&App.xaml.cs)
App.xamlはXAMLというマークアップ言語で記述されたファイルです。
App.xamlには以下のような記述がされています。
<Application x:Class="Hello_UWP.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Hello_UWP" RequestedTheme="Light"> </Application>
xamlファイルは基本的にプログラムコードを記述できる拡張子がxaml.cs(C#の場合)のファイルとセットになっています。上記画像のようにApp.xamlにはApp.xaml.csが対になっています。もし、App.xaml.csが表示されていない場合はApp.xamlの左側の三角印をクリックすると表示されます。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; namespace Hello_UWP { /// <summary> /// 既定の Application クラスを補完するアプリケーション固有の動作を提供します。 /// </summary> sealed partial class App : Application { /// <summary> /// 単一アプリケーション オブジェクトを初期化します。これは、実行される作成したコードの ///最初の行であるため、main() または WinMain() と論理的に等価です。 /// </summary> public App() { this.InitializeComponent(); this.Suspending += OnSuspending; } /// <summary> /// アプリケーションがエンド ユーザーによって正常に起動されたときに呼び出されます。他のエントリ ポイントは、 /// アプリケーションが特定のファイルを開くために起動されたときなどに使用されます。 /// </summary> /// <param name="e">起動の要求とプロセスの詳細を表示します。</param> protected override void OnLaunched(LaunchActivatedEventArgs e) { #if DEBUG if (System.Diagnostics.Debugger.IsAttached) { this.DebugSettings.EnableFrameRateCounter = true; } #endif Frame rootFrame = Window.Current.Content as Frame; // ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返さずに、 // ウィンドウがアクティブであることだけを確認してください if (rootFrame == null) { // ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動します rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: 以前中断したアプリケーションから状態を読み込みます } // フレームを現在のウィンドウに配置します Window.Current.Content = rootFrame; } if (rootFrame.Content == null) { // ナビゲーション スタックが復元されない場合は、最初のページに移動します。 // このとき、必要な情報をナビゲーション パラメーターとして渡して、新しいページを //構成します rootFrame.Navigate(typeof(MainPage), e.Arguments); } // 現在のウィンドウがアクティブであることを確認します Window.Current.Activate(); } /// <summary> /// 特定のページへの移動が失敗したときに呼び出されます /// </summary> /// <param name="sender">移動に失敗したフレーム</param> /// <param name="e">ナビゲーション エラーの詳細</param> void OnNavigationFailed(object sender, NavigationFailedEventArgs e) { throw new Exception("Failed to load Page " + e.SourcePageType.FullName); } /// <summary> /// アプリケーションの実行が中断されたときに呼び出されます。 /// アプリケーションが終了されるか、メモリの内容がそのままで再開されるかに /// かかわらず、アプリケーションの状態が保存されます。 /// </summary> /// <param name="sender">中断要求の送信元。</param> /// <param name="e">中断要求の詳細。</param> private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); //TODO: アプリケーションの状態を保存してバックグラウンドの動作があれば停止します deferral.Complete(); } } }
通常xamlとxaml.csはこの後紹介するMainPage.xamlのように画面または画面の一部を定義するのですが、このApp.xaml(&App.xaml.cs)は異なり、App.xamlにはアプリケーションで利用できる共通のXAMLリソースを、App.xaml.csには「アプリケーションの起動時や終了時」の動作を記述します。
App.xaml.csのコードについては後述します。
・{プロジェクト名}_TemporaryKey.pfx
アプリケーションの証明書情報ファイルです。開発中に触ることはありません。
・MainPage.xaml(&MainPage.xaml.cs)
最初に表示される画面とその動きを定義します。
MainPage.xamlにXAMLで画面定義を記述し、MainPage.xaml.csにページの動きをC#コードで記述します。xaml.csはコードビハインドとも呼ばれます。
・Package.appxmanifest
アプリケーションの情報を記述するマニフェストファイルです。
Package.appxmanifestにはアプリ名や、アプリのロゴアイコンの指定、利用する機能、バージョン情報などを記述します。
・project.json
プロジェクトの情報をjson形式で記述します。
{ "dependencies": { "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0" }, "frameworks": { "uap10.0": {} }, "runtimes": { "win10-arm": {}, "win10-arm-aot": {}, "win10-x86": {}, "win10-x86-aot": {}, "win10-x64": {}, "win10-x64-aot": {} } }
作成した直後のプロジェクトのファイル構成を確認しました。続いて、App.xaml.csやMainPage.xaml.csを読み解きながら、アプリケーションの動作を見ていきましょう。
・App.xaml
アプリケーションの動作ですが、まずApp.xaml.csの処理が実行されます。App.xaml.csに記述されたAppクラスを起点となるエントリポイントとも呼びます。
Appクラスが生成されたのち、OnLaunchedメソッドが呼び出されます。
/// <summary> /// アプリケーションがエンド ユーザーによって正常に起動されたときに呼び出されます。他のエントリ ポイントは、 /// アプリケーションが特定のファイルを開くために起動されたときなどに使用されます。 /// </summary> /// <param name="e">起動の要求とプロセスの詳細を表示します。</param> protected override void OnLaunched(LaunchActivatedEventArgs e) { #if DEBUG if (System.Diagnostics.Debugger.IsAttached) { this.DebugSettings.EnableFrameRateCounter = true; } #endif Frame rootFrame = Window.Current.Content as Frame; // ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返さずに、 // ウィンドウがアクティブであることだけを確認してください if (rootFrame == null) { // ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動します rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: 以前中断したアプリケーションから状態を読み込みます } // フレームを現在のウィンドウに配置します Window.Current.Content = rootFrame; } if (rootFrame.Content == null) { // ナビゲーション スタックが復元されない場合は、最初のページに移動します。 // このとき、必要な情報をナビゲーション パラメーターとして渡して、新しいページを //構成します rootFrame.Navigate(typeof(MainPage), e.Arguments); } // 現在のウィンドウがアクティブであることを確認します Window.Current.Activate(); }
OnLaunchedメソッドの引数で受け取るLaunchActivatedEventArgsは起動時の情報が格納されています。
#if DEBUG if (System.Diagnostics.Debugger.IsAttached) { this.DebugSettings.EnableFrameRateCounter = true; } #endif
上記行はデバッグ実行時にフレームレートカウンターを表示するという指定です。
フレームレートカウンターを表示すると、アプリケーションのフレームレートが表示されパフォーマンスの確認ができます。
画面の確認にフレームレートカウンターが邪魔な場合は上記コードを編集しましょう。
#if DEBUG if (System.Diagnostics.Debugger.IsAttached) { // フレームレートカウンターを表示しない this.DebugSettings.EnableFrameRateCounter = false; } #endif
続いて、アプリケーションのフレームを用意します。MainPage.xamlが一画面を定義するのに対して、フレームは画面などを表示するアプリケーション全体というイメージです。
Frame rootFrame = Window.Current.Content as Frame; // ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返さずに、 // ウィンドウがアクティブであることだけを確認してください if (rootFrame == null) { // ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動します rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: 以前中断したアプリケーションから状態を読み込みます } // フレームを現在のウィンドウに配置します Window.Current.Content = rootFrame; }
e.PreviousExecutionStateからアプリケーションが中断からの起動かどうかを確認しています。アプリケーションのライフサイクルについては別の回に紹介しますが、このように初期起動か、中断されたアプリケーションの再開かで処理を変えることができる点を覚えておいてください。
フレームの用意ができたら画面を表示します。
下記コードにMainPageという文字があることに注目してください。
if (rootFrame.Content == null) { // ナビゲーション スタックが復元されない場合は、最初のページに移動します。 // このとき、必要な情報をナビゲーション パラメーターとして渡して、新しいページを //構成します rootFrame.Navigate(typeof(MainPage), e.Arguments); } // 現在のウィンドウがアクティブであることを確認します Window.Current.Activate();
MainPageの部分を他のページを表すXAMLに変更すれば別のページに変更できそうなことや、ページの変更はFrameのNavigateメソッドを使うことが推測できます。
プログラムの流れに従い、ページを表すMainPage.xamlの記述を見ていきましょう。
・MainPage.xaml
MainPage.xamlはマークアップ言語のXAMLで画面を定義したテキストファイルですが、Visual Studioで開くと以下の画像のようにボタンなどを視覚化した画面とXMLコードを記述した画面が表示されます。
これはHTMLのソースファイルと、それをブラウザで見た画面の関係に似ています。Visual StudioではXAMLコードのXMLを解析した結果「こういう画面表示になる」という結果をビジュアルエディタ画面に表示してくれます。ビジュアルエディタ側でボタンの位置を変更した結果は、XAMLコードで書かれたコードエディタ側に反映されます。逆にコードエディタに加えた変更に合わせてビジュアルエディタの視覚化した画面も変更されます。
開発者がXAMLを編集する場合、どちらを編集しても構いませんが、細かいピクセル単位の修正はコードエディタでコードの数値を変更した方が容易であり、新しいコントロールの追加などはビジュアルエディタ上にドロップした方が楽という操作のしやすさはありますので、自分で触りながら自分に合った編集方法を身に着けて行くと良いでしょう。
MainPage.xamlの内容に戻ると、ここにはボタンを表すButtonコントロールと、テキストを表すTextBlockコントロールが追加されています。
<Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="95,301,0,0" VerticalAlignment="Top" Click="button_Click"/> <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="95,231,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
それぞれのコントロールには「x:Name」プロパティとして固有の名前が指定されています。このx:Nameの値はMainPage.xaml.cs(コードビハインド)側でも利用するので覚えておきましょう。
また、Buttonコントロールには「ボタンがクリックされた場合」というイベント発生時の動作を表す「Click=”button_Click”」という記述があります。こちらもMainPage.xaml.csに登場します。
それでは、ページの動きを記述したMainPage.xamlを見ていきます。
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } private void button_Click(object sender, RoutedEventArgs e) { this.textBlock.Text = "Hello World"; } }
Hello Worldではボタンを押した際の動作を表すbutton_Clickメソッドのみを追記しました。button_Clickメソッドはイベントハンドラーとも呼ばれます。
第一引数のobjectはイベントが発生したオブジェクト(今回はbuttonコントロール)が格納され、RoutedEventArgsはイベントの情報が格納されています。
button_Clickメソッドの処理は、textBlock(とx:Name属性で指定したTextBlockコントロール)のTextプロパティに”Hello World”という文字列を渡しています。
TextBlockコントロールのTextプロパティには画面に配置されたTextBlockコントロールの表示文字を指定するので、ボタンを押すと画面の文字がHello Worldに変更されるという流れです。
Hello Worldアプリケーションの中身を簡単に眺めてみました。UWPの起動からページ表示の流れや、ボタンを押した際のイベントハンドラーの書き方などが学習できたと思います。
この後は、UWPで良く利用する処理のサンプルを紹介していこうと思います。
Please give us your valuable comment