皆様、こんにちは!ちょっと間が空いてしまいましたが、デモ解説の #3、ページ間のナビゲーションについてご紹介します。まずは今回も冒頭はスライドシェアのリンクですね。
<Windows 8.1 におけるストア ビジネスアプリの設計と開発>
Windows 8.1 と Prism を使ったストアアプリのページナビゲーションについて
要は、NavigationService の使い方を、アプリに実装するということですが、今回は英国の同僚が公開しているアプリをもとに実装したもので、アプリとしてはそれなりに体裁の整ったものになっていますので、このままご紹介していきます。
セッションでは、ナビゲーションサービスの説明として、下記の2つのコード断片をご紹介しました。
アプリのスタートアップ時のナビゲーション(App.xaml.cs)
1:protectedoverride Task OnLaunchApplication(LaunchActivatedEventArgs args)
2: {
3:this.NavigationService.Navigate("Search", null);
4:
5:return (Task.FromResult<object>(null));
6: }
検索ボタンに割り当てられたコマンドによるナビゲーション(SearchPageViewModel.cs)
1:void OnSearchCommand()
2: {
3:this._navigationService.Navigate("SearchResults", this.SearchTerm);
4: }
あとは前の2回の解説を見ればわかりますが、今回は少し詳細にアプリにつき、解説してあります。
まず、ソリューションファイルはこちらからダウンロードしてください(セッションでもご紹介した通り実アプリではなくあくまでサンプルです)。起動して実行する方は、Flickr (Yahoo!の画像共有サービスですね)の ID で API Key を取ってくることを忘れずに!
セッションでご紹介したデモアプリは、下記の通りの構成でした。3画面があり、Flickr から、写真を検索して、検索結果を表示して、個別の写真詳細を表示するアプリです。
検索画面はこんな感じですね。背景画像は MSN で公開されている壁紙を使用しました。
Image may be NSFW.
Clik here to view.
ちなみにこの検索ワードは、私が愛用している E.Guitar と同じモデル、Ibanez RG 550 Genesis Collection です。検索結果はこんな感じです。
Image may be NSFW.
Clik here to view.
そして、写真を1枚タッチすると、詳細はこんな感じです。
Image may be NSFW.
Clik here to view.
加えてアプリバーを下からスワイプすると、ユーザー画像ライブラリに保存できる、というボタンがあります。
Image may be NSFW.
Clik here to view.
このアプリは、IoC(Inversion of Control )コンテナとして、”NuGet パッケージの管理”から、.NET 4.5 用に最適化されている AutoFacというものを入れてあります。これは便利で、最近ストアアプリでの利用がどうやら?推奨されているようです。
※ Prism のテンプレートに、IoC コンテナとして、Unityを使っているものも含まれていますが、それと似たようなものですね。Prism テンプレートはこちらから。インストールは簡単です。その後、Visual Studio を再起動してください。
。Image may be NSFW.
Clik here to view.
こちらが AutoFac がダウンロードできるサイトです。NuGet からもインストールできます。
Image may be NSFW.
Clik here to view.
Inversion of Controlとは、Dependency Injection(依存性の注入)と同義です。すなわち、自分で作成したコンポーネントから Framework を呼び出すのではなく、Framework からコンポーネントを呼び出してくれます。依存性の注入というのは(いつも思うのですが)分かりにくい日本語です。要は、設定ファイルにオブジェクト間の連携を記述しておけば、あるオブジェクトに対して使うオブジェクトがセットされ、そのオブジェクトの利用者は自らインスタンス生成をしなくて良い、ということになるわけです。
さて、ここではView は3つあります。先述した通り、SearchPage、SearchResultsPage、そして、PhotoDetailsPageです。
また、他に2つのサービス��あり、Windows 8/8.1 固有のものです。1つは、写真をピクチャライブラリに保存するもので、もう1つは、ナビゲーションサービスの特性を抽象化して各 ViewModel から使えるようにするためのものです。
Autofacコンテナーを準備し、Abstractionsライブラリフォルダに入っているインタフェースにより抽象化されたサービスの実装をこれを使って行います。ViewModel をインスタンス化し、それらを View の DataContext にバインディングします。
ここでは、(ベースクラスに加えて) 4 つの ViewModel を準備し、3つの Views をサポートします。
1. SearchPageViewModel
2. SearchResultsPageViewModel および SearchResultViewModel (この ViewModel は、各々のView の中に表示される GridView にバインドされます)
3. PhotoDetailsPageViewModel
もう一つ、Modelクラスがあります。これが、FlickrSearchItem です。これは、Flickr から取ってきたデータをデシリアライズしたデータを表示するものです。コードとしては、FlickR の API を使って検索し、結果を取得して、Modelクラスのインスタンスを List に入れて返します。
このコードは (ポータブル HttpClientライブラリの利用により) 、極めて可搬性に優れています。 したがって、すぐに抽象化をする必要はないのですが、抽象化した方が良いと思われます。これに依存性を持つクラスに注入できるからです。
また、このライブラリの中に ICommandがあり、SimpleCommandクラスの中にあります。このクラスは、ViewModelBaseと呼ばれる ViewModel のベースクラスで、 INotifyPropertyChangedを実装しています。
これらのインタフェースは、ナビゲーションサービスや写真の保存サービスを抽象化するもので、それら特定のサービス作成に伴い実装されます。
※ この辺りまでは、MVVM、Commanding、Binding、Abstraction、IoC、などを知っていることが前提になっていますが、ここではこれ以上個別に詳述はしません。もし「これらを初めて聞いた」、という場合は、この Prism for Windows Runtime のドキュメントやリンク先の URL 等にも書かれていますので、そちらを参照してくださいませ。
スクラッチでアプリを作成してPrism for Windows Runtime を追加
さてここからアプリを作成します。前回のアプリ同様、Visual Studio 2013 で、最初にテンプレートから、無印新規アプリを選択し生成、その上で、NuGetからオンラインで、Prism.StoreAppsパッケージを追加します。その後、アセットフォルダに画像を配置します。MSN等の壁紙からダウンロードする等して適切な大きさに加工したうえで、物理フォルダーにコピーします。その後、マニフェストファイルを開いて適切に設定します(例:アイコン用、スプラッシュスクリーンの画像用、等々)。
できるだけシンプルにしたいので、一つのプロジェクトの中に、必要なフォルダーを名前を付けて作成します。
Image may be NSFW.
Clik here to view.
できあがったところで、今回は必要ないので、MainPage.xaml(と分離コード)を削除します。
まずは、Appクラスのベースクラスを変更します。Visual Studio 無印新規アプリテンプレートの標準の、Applicationから、Prism の MvvmAppBaseに変更します。
次に、ViewModel フォルダーに移動します。オリジナルの ViewModelBaseベースクラスを作り、それを Prism のオリジナル ViewModelクラスに置換します。同時に、FlickrServiceコードは、そのコードを抽象化するインターフェースを使用して作成したもので、それが一つの ViewModelを構成しています。すなわち当該 ViewModel は、実装それ自体に依存するよりは、(少なくとも構築時には)当該インターフェースに依存したインターフェースに依存します。もう一つの ViewModel に関する注意点は、ナビゲーションサービスに依存するものであったということです。このナビゲーションサービスは、独自に実装したもので、Prism for Windows Runtimeが提供する INavigationServiceに依存するものです。そして、2つの ViewModel において、ViewModel.OnNavigatedToメソッドをオーバーライドし、ナビゲーション操作により受け渡されるパラメーターを受け取れるようにします。
Prism の NavigationServiceを使う場合には、シンプルに下記のように書きます。
1:void OnSearchCommand()
2: {
3:this._navigationService.Navigate("SearchResults", this.SearchTerm);
4: }
ここで、“SearchResults”は抽象化されたトークンで、SearchResultsPageページへ遷移するのに使われます。
ここでオリジナルの SimpleCommandクラスを使い、これにより、 Prism の DelegateCommandを置換します。ほぼ同じように動作します。
そして、ViewModelと DataContextの値の設定をおこなうためにコードを書きます。ついで、ページのベースクラスを Prism の VisualStateAwarePageに変更します。最終的に XAMLの中で 各 Viewが宣言的に作成され、Prism の ViewModelLocator.AutoWireVIewModelプロパティを Trueに設定することになります。これで全部のパーツがつながることになります。
1.アプリを起動すると、SearchPageの View に遷移します(NavigationService )。
2. Navigationで使われるのは、 “Search”のような文字列(論理名)です。これは、正確に SearchPageのようなページ (View) 名に解決されます。
3. Viewは ViewModelを生成し、そして ViewModelは、それが依存するサービスの名前解決をします。
これれらとは別に、App クラスを、これらに合致するような形に変更しておく必要があります。上記の 1.で、OnLaunchApplicationメソッドをオーバーライドしておく必要があります。
1:protectedoverride Task OnLaunchApplication(LaunchActivatedEventArgs args)
2: {
3:this.NavigationService.Navigate("Search", null);
4:
5:return (Task.FromResult<object>(null));
6: }
そして、2.において、GetPageTypeメソッドをオーバーライドします。これは、デフォルトの実装が、例えばページトークンが“Main”のようなものだと、その名前を元にそれを “MainPage”タイプに名前解決しようとするためです。この理由は、この “MainPage” クラスの名前空間が、現在実行されているアセンブリの名前空間となることが通常期待されるからです。しかし、このアプリの Viewはそのように名前付けされていませんので、オーバーライドした訳です。
1:protectedoverride Type GetPageType(string pageToken)
2: {
3:string baseName = "MvvmStart.Views.{0}Page";
4:string typeName = string.Format(baseName, pageToken);
5: Type type = Type.GetType(typeName);
6:return (type);
7: }
そして、最後のポイント上の 3. で、Autofacコンテナーを使い、 OnInitializeメソッドをオーバーライドしています。
1:protectedoverridevoid OnInitialize(IActivatedEventArgs args)
2: {
3:base.OnInitialize(args);
4:
5: ContainerBuilder builder = new ContainerBuilder();
6:
7: builder.
8: RegisterInstance<INavigationService>(this.NavigationService).
9: As<INavigationService>();
10:
11: builder.RegisterType<FlickrService>().As<IFlickrService>();
12: builder.RegisterType<PhotoSavingService>().As<IPhotoSavingService>();
13:
14: builder.RegisterType<SearchPageViewModel>().AsSelf();
15: builder.RegisterType<SearchResultsPageViewModel>().AsSelf();
16: builder.RegisterType<PhotoDetailsPageViewModel>().AsSelf();
17: builder.RegisterType<SearchResultViewModel>().AsSelf();
18:
19:this._container = builder.Build();
20: }
Autofacに詳しい訳ではないので、この使い方で良いかはわかりません。しかし、これにより、基本的にナビゲーションサービス、Flickr サービス、写真保存サービスが登録されます。そして、ViewModelが、それぞれの依存するサービスを解決した形のコンテナーから生成されるのです。
そこで、これらのコンテナー群を使うために、Resolve()メソッドをオーバーライドしておきます。
1:protectedoverrideobject Resolve(Type type)
2: {
3:return (this._container.Resolve(type));
4: }
以上です。
次に、SearchResultsPageViewModelを作成します。これは、SearchResultViewModelのコレクションを持っています。この SearchResultViewModelは、コンテナーからのSearchResultViewModel作成の仕方や、その依存性の解決の仕方を保持しています。AutoFacが全体的にはうまくやってくれますので、SearchResultsPageViewModelのコンストラクターはこのようになります。
1:public SearchResultsPageViewModel(
2: INavigationService navigationService,
3: IFlickrService flickrService,
4: Func<SearchResultViewModel> resultViewModelFactory)
5: {
6:this._navigationService = navigationService;
7:this._flickrService = flickrService;
8:this._resultViewModelFactory = resultViewModelFactory;
9: }
以上です。
次は、#4として、データ���クセス・認証、疎結合コンポーネント間通信、の箇所を纏めて取り上げ、ついで、#5 として、ユーザー入力の検証(バリデーション)のところを取り上げて、このセッションの解説を終了したいと思います。
それではまた!
鈴木章太郎
Image may be NSFW.Clik here to view.