これから何回かに分けて、WindowsTemplateStudioで生成したプロジェクトが、どのような構造になっているか見ていこうと思います。
今回は最初なので、UWPの基本的な仕組みも含めて色々と試してみます。
プロジェクトの作成
WindowsTemplateStudioの雛形で、以下の構成でプロジェクトを作成します。
- ProjectType: Blank
- Framework: Code Behind
- Pages, Features: デフォルトのまま
全体の構成
まずは、プロジェクト全体の構造のおさらい。
こんなプロジェクトが生成されます。
この構成のプロジェクトでは、主にViewsやModelsフォルダにコードを書いていくことになりそうな雛形ですね。
アプリのスタートアップ処理
で、この辺の処理ではActivationService/ActivationHandlerなどのクラスが色々と出てきます。
この辺の処理は複雑なので、スタートアップ処理全体の流れは、一番最後にまとめて確認したいと思います。
まずはAppクラスの中身を確認します。
最初にチェックしておくポイントは、以下の関数。
このActivationServiceのコンストラクタで、第二引数に渡している型が、最初に表示されるページになります。
/// <summary>/// Provides application-specific behavior to supplement the default Application class./// </summary>sealedpartialclass App : Application { // :// 略// :private ActivationService CreateActivationService() { returnnew ActivationService(this, typeof(Views.MainPage)); } }
ページ表示
MainPageクラス
今回のサンプルは、MVVMは使わず、コードビハインドを利用した雛形となっています。
そのため、ViewModelクラスはなく、以下のようにMainPageクラスでINotifyPropertyChangedインターフェースを実装しています。
Setメソッドも用意されていて、PropertyChangedイベントを呼び出す処理が実装されています。
publicsealedpartialclass MainPage : Page, INotifyPropertyChanged { public MainPage() { InitializeComponent(); } publicevent PropertyChangedEventHandler PropertyChanged; privatevoid Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null) { if (Equals(storage, value)) { return; } storage = value; OnPropertyChanged(propertyName); } privatevoid OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
MainPageへの処理追加
このページのコードをちょっと修正し、以下のようなサンプルを作ってみたいと思います。
- TextBoxで文字列を入力
- ↑の文字列をTextBlockで表示
- ボタンを押すと、TextBoxの入力内容をクリア
実行するとこんな感じの画面になります。
string型のInputプロパティを作り、ボタンクリック時にはそのプロパティをクリアするだけのサンプルです。
MainPage.xaml/MainPage.xaml.csを、それぞれ以下のように修正します。
MainPage.xaml
<Pagex:Class="Blank_CodeBehind.Views.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><Gridx:Name="ContentArea"Margin="12,0,12,0"><Grid.RowDefinitions><RowDefinition x:Name="TitleRow"Height="48"/><RowDefinition Height="*"/></Grid.RowDefinitions><TextBlockx:Name="TitlePage"x:Uid="Main_Title"Text="Navigation Item 2"FontSize="28"FontWeight="SemiLight"TextTrimming="CharacterEllipsis"TextWrapping="NoWrap"VerticalAlignment="Center"Margin="0,0,12,7"/><Grid Grid.Row="1"Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}"><StackPanel><TextBox Margin="5"Width="120"Text="{x:Bind Input, Mode=TwoWay}"HorizontalAlignment="Left" /><TextBlock Text="{x:Bind Input, Mode=OneWay}" /><Button Content="Clear"HorizontalAlignment="Left"Margin="5"Click="Button_Click"/></StackPanel></Grid></Grid></Page>
MainPage.xaml.cs
publicsealedpartialclass MainPage : Page, INotifyPropertyChanged { public MainPage() { InitializeComponent(); this.DataContext = this; } privatestring input; publicstring Input { get { return input; } set { this.Set(refthis.input, value); } } privatevoid Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) { this.Input = string.Empty; } publicevent PropertyChangedEventHandler PropertyChanged; privatevoid Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null) { if (Equals(storage, value)) { return; } storage = value; OnPropertyChanged(propertyName); } privatevoid OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
ページ遷移
ここで作った雛形では、単一のMainPageしか用意されていません。
自分で新規ページを追加すると、以下のような手順でページ遷移が行えます。
プロジェクト作成時のウィザードで、複数のページを作っていた場合も、同様の方法でページ遷移できます。
ページの追加
Viewsフォルダを右クリックし、メニューから「新しい項目」を選んでクリックします。
「空白のページ」を選んで、新しいページをプロジェクトに追加しましょう。
このページには、以下のようにTextBlockでのメッセージだけ追加しておきます。
BlankPage1.xaml
<Page x:Class="Blank_CodeBehind.Views.BlankPage1"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="using:Blank_CodeBehind.Views"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><TextBlock Margin="5"HorizontalAlignment="Left"VerticalAlignment="Top"Text="This is BlankPage1.xaml"TextWrapping="Wrap" /></Grid></Page>
追加したページへの移動
MainPageに以下のようなボタンを追加します。
MainPage.xaml
<Button Content="Navigate"HorizontalAlignment="Left"Margin="5"Click="GoToBlankPage"/>
ボタンクリック時のイベントハンドラは、以下のようにします。
NavigationServiceクラスを用いて、ページ遷移の指示を行います。
MainPage.xaml.cs
privatevoid GoToBlankPage(object sender, Windows.UI.Xaml.RoutedEventArgs e) { Services.NavigationService.Navigate<BlankPage1>(); }
Navigateボタンをクリックすると、ページ遷移します。
ページ遷移すると、ウィンドウ左上に「←」ボタンが出るので、このボタンでページを戻ることができます。
戻る/進む
先ほどのサンプルでは、自動で表示される「←」ボタンでページを戻りましたが、
NavigationServiceの以下のメソッドで、ページを戻る/進む、という動作を実行できます。
戻る処理
if (Services.NavigationService.CanGoBack)
{
Services.NavigationService.GoBack();
}
進む処理
if (Services.NavigationService.CanGoForward)
{
Services.NavigationService.GoForward();
}
ローカライズ処理
x:Uid属性とreswファイルを用いたローカライズ
UWPでは、reswファイルを用いたローカライズが行えます。
詳細は、↓のページなどに詳しくまとまっています。
http://www.atmarkit.co.jp/ait/articles/1210/25/news026.html
http://aile.hatenablog.com/entry/2016/04/05/224806
雛形のデフォルトの状態だと、「en-us」のリソースのみが用意されています。
ここでは、「ja-jp」のリソースを追加して、日本語環境用の表示文字列を定義してみます。
ja-jpフォルダを作成し、その中に「リソースファイル(.resw)」を作成します。
そして、以下のように「Main_Title.Text」の項目に、日本語環境で表示したい文字列を設定します。
実行するとこの通り。
これだけで、x:Uid="Main_Title"
の要素を日英対応するようローカライズできました。
言語設定の切り替え
ここで行ったローカライズでは、OSの表示言語設定に応じてアプリ表示の文字列が切り替わります。
しかし、OSの表示言語設定を切り替えるのは少し面倒です。
ちょっとだけ別言語での表示を確認したい、というようなときは、以下のようにAppクラスのOnLaunchedイベントで言語設定を切り替えてしまえば、別言語環境で表示される文字列をチェックでできます。
protectedoverride async void OnLaunched(LaunchActivatedEventArgs e) { // 表示言語の設定 System.Globalization.CultureInfo.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); if (!e.PrelaunchActivated) { await ActivationService.ActivateAsync(e); } }
この方法だと、言語設定が完全には切り替わりません。
ですので、reswファイルで設定した文字列のチェック程度にとどめておき、画面キャプチャなどが必要な場合には実際にOSの表示言語を切り替えた方がよいかと思います。
ヘルパークラス類
ResouceExtensionsクラス
x:Uidやreswを使うと、各種コントロールなどに表示する文字列は簡単にローカライズできます。
しかし、コードビハインドなど、C#コードからreswに定義した文字列にアクセスしたくなる場合もあると思います。
このResourceExtensionsクラスは、reswの内容へのアクセスを補助するヘルパークラスです。
呼び出し方はこんな感じ。
var text = Helpers.ResourceExtensions.GetLocalized("Main_Title/Text"); var dlg = new MessageDialog(text); await dlg.ShowAsync();
.reswファイルで、Main_Title.Text
のように「.」区切りで書いていた部分は、「/」に置き換えてアクセスする必要があるので注意!!
Singletonクラス
一応、こんな風に任意のクラスをシングルトンとして扱えるようにする、汎用的なヘルパークラスです。
private async void Button_Click_1(object sender, Windows.UI.Xaml.RoutedEventArgs e) { var person = Helpers.Singleton<Person>.Instance; System.Diagnostics.Debug.WriteLine(person.Name); } publicclass Person { publicstring Name { get; set; } publicint Age { get; set; } }
スタートアップ処理の全体像
ActivationService & ActivationHandlerクラス
WindowsTemplateStudioで生成されるコードで、一番の鬼門になるのはこの辺かと思います。 ちょっとクラス構成も入り組んでますし、各関数の呼ばれるタイミングも、少しわかりにくいです。
ということで、GitHubのリポジトリでも、↓にこのActivationService周りの説明をするページが用意されていました。 https://github.com/Microsoft/WindowsTemplateStudio/blob/master/docs/activation.md
アプリを普通に起動するのではなく、サスペンドからの復帰だったり、ファイル関連付けからの起動などなど、
アプリがアクティブになる、様々なパターンを扱いやすくするようにこんな構造になっているようです。
クラス構成
アプリの起動処理に関わるクラス構成は、以下のようになっています。
これらのクラスが、以下のようなシーケンスで呼び出されて、初期画面のMainPageが表示されます。
少々複雑な構成ですが、ここさえ理解できれば、このテンプレートでは他に難しいところは特にないかと思います。
基本的には、ActivationHandler派生のクラスを作成し、作成したクラスをActivationSerivceに登録することで、起動処理をカスタマイズできるような構成になっています。
アプリ起動処理のカスタマイズ(ファイルの関連付け)
このアプリを、ファイル関連付けから起動できるようにカスタマイズしてみます。
まずは、プロジェクトのプロパティから、「パッケージマニフェスト」の画面を開き、以下のように赤線の部分を設定します。
続いて、App.xaml.csでOnFileActivated
イベントの処理を追加します。
ファイル関連付けから起動された場合にも、ActivationServiceでの起動処理が実行されるようにしています。
App.xaml.cs
protectedoverride async void OnFileActivated(FileActivatedEventArgs args) { //base.OnFileActivated(args); await ActivationService.ActivateAsync(args); }
次に、FileAssociationServiceというクラスをServicesフォルダに追加します。
このファイルの内容は以下の通り。
FileAssociationService.cs
internalclass FileAssociationService : ActivationHandler<File<200b>Activated<200b>Event<200b>Args> { protectedoverride async Task HandleInternalAsync(FileActivatedEventArgs args) { // ファイルの情報を表示 var file = args.Files.FirstOrDefault(); var dlg = new MessageDialog($"{file.Name}を指定して、起動されました。"); await dlg.ShowAsync(); // MainPageへと遷移 NavigationService.Navigate(typeof(MainPage)); await Task.CompletedTask; } }
最後に、ActivationSerivceクラスに以下の行を追加し、アクティベーション処理の中で、先ほど追加したFileAssociationServiceのインスタンスを登録しておきます。
ActivationService.cs
private IEnumerable<ActivationHandler> GetActivationHandlers() { yieldreturn Helpers.Singleton<FileAssociationService>.Instance; // ←追加yieldbreak; }
ここまで出来たら、一度ビルドしてから実行しておきます。
動作確認
作ったアプリを一度実行しておくと、以下のように「プログラムから開く」のメニューに、このアプリが追加されています。
ここからアプリを起動すると、先ほど作成したFileAssociationServiceの処理が実行されていることが確認できます。
ファイル関連付けの解除
作成したアプリをアンインストールすると、「プログラムから開く」メニューの項目は自動で削除されます。
今回はここまで。
次は、MVVM Basicなプロジェクトを見ていきたいと思います。