prism + reactiveproperty入門
TRANSCRIPT
Windows Phone開発のためのPrism + ReactiveProperty基本
2015/6/26 まんつーまん@調布
大田 一希
Prismとは
• Microsoft Patterns & Practicesチーム製のMVVMフレームワーク• 以下のプラットフォームをサポート
• WPF(超複雑)
• Silverlight(オワコン)
• Windows store app
• Windows Phone(今回の主題)
• Xamarin(Preview)
•最近MS P&PチームからMVP主体のチームに移管された
Prism.StoreAppsPrism.StoreAppsの中から電話向けに説明
Prism.StoreApps
• Universal app(Windows store app + Windows Phone app)版Prism
•以下の構成要素がある• Prism.Mvvm :MVVMをサポートする基本クラスを提供
• Prism.Mvvm.Phone :↑にPhone固有機能を提供
• Prism.StoreApps :Win store app + WP app固有機能を提供
• Prism.StoreApps.Phone:↑にWP固有機能を提供
覚えておきたいクラス
Prism.Mvvm
•本当にプレーンなMVVMの必要最低限の機能を提供するライブラリ
•以下の機能を提供• BindableBase class :INotifyPropertyChangedの基本実装
• DelegateCommand class :ICommandの基本実装
Prism.Mvvm.Phone
•以下の機能を提供• ViewModelLocator class :ViewとViewModelの紐づけ機能
Prism.StoreApps
• PrismのUniversal app版
•以下の機能を提供• VisualStateAwarePage class :ページの基本クラス
• ViewModel class :ViewModelの基本クラス
• SessionStateService class :中断時の一時データ保存用クラス
• FrameNavigationService class :画面遷移用クラス
Prism.StoreApps.Phone
• Prism.StoreAppsのPhone特化版
•以下の機能を提供• MvvmAppBase class :Appクラスの基本クラス
クラス詳解各クラスの使い方
BindableBase class
• MVVMには必須のINotifyPropertyChangedの実装クラス
• SetProperty<T>メソッドを使う
こんな感じにsetが書ける
コードスニペットに登録すると捗る
DelegateCommand class
• ReactiveProperty使うならいらない子なので割愛
ViewModelLocator class
•命名規約(カスタマイズ可能だけどやりかたは割愛)によりViewとViewModelを紐づける
•以下のような命名規約• AppNamespace.Views.HogePage(View名)
• AppNamespace.ViewModels.HogePageViewModel(View名+ViewModel)
この2つを結びつける
ViewModelLocator class
• ページの添付プロパティとして記述することで機能する
VisualStateAwarePage class
• Pageに便利機能を追加
• Prismを使う上でページはこいつを継承する必要がある
•以下の機能を提供• GoBackCommand property :戻る機能のCommand
• GoHomeCommand property :トップページへ戻る機能のCommand
• SaveState method :オーバーライドして中断時のデータ保存
• LoadState method :オーバーライドして中断時のデータ読込
• VisualStateの切り替え• DefaultLayout, PortraitLayout, MinimalLayoutを自動で切り替え(詳細は割愛)
VisualStateAwarePage class
•以下のように”空白のページ”テンプレートを書き換える• ViewModelLocatorも合わせて以下のようなXAMLがひな形
VisualStateAwarePageの名前
空間を定義
Pageタグを書き換え
VisualStateAwarePage class
•以下のように”空白のページ”のテンプレートを書き換える
OnNavigatedToメソッドのオー
バーライドを消す
基本クラスを
VisualStateAwarePageにする
ViewModel class
• BindableBaseを拡張して以下の機能を提供する• OnNavigatedToメソッド :画面遷移してきたときに呼ばれる
• OnNavigatedFromメソッド :画面から離れるときに呼ばれる
• 中断処理への対応 :RestorableState属性つけたプロパティの自動復元
ViewModel class
•基本以下のような書き方になる
SessionStateService class
•中断時のデータ保存用クラス
•実際に使うときはISessionStateService interface
•後述するMvvmAppBaseのプロパティとして定義される• Dictionary<string, object> SessionState { get; }プロパティがある
• ↑は、中断時のデータ保存場所
• RegisterKnownTypeメソッド• プリミティブ型以外をSessionStateに登録するときに型を登録するメソッド(しておかないと例外が飛ぶ)
FrameNavigationService class
•画面遷移をするためのクラス
•実際使うときはINavigationService interface
• MvvmAppBaseクラスのプロパティとして定義される
FrameNavigationService class
•画面遷移の仕方• ViewのXXXXPageに遷移するときは以下のように呼び出す
• NavigationService.Navigate(“XXXX”, null); // 第二引数は画面遷移パラメータ
• Frameにあるようなメソッドがある• GoBack() :戻る
• CanBoBack() :戻れる場合true
MvvmAppBase class
• Appクラスの基本クラス
• なが~いApp.xaml.csをシンプルに書ける
MvvmAppBase class
• Appクラスの定義方法
• App.xamlを以下のように書き換える
MvvmAppBaseの名前空間を
定義
Appタグを書き換え
MvvmAppBase class
• MainPageに遷移する場合のコードビハインド
長い処理を消して画面遷移す
るだけの処理へ
基本クラスをMvvmAppBaseへ
組み合わせて使う
Pageで画面遷移するには
• MvvmAppBaseのOnInitializeAsyncメソッドをオーバーライドする
• Pageに紐づくViewModelの生成をカスタマイズする• ViewModelにNavigationServiceを渡す
• MainPageViewModelの生成をカスタマイズする場合は以下のような感じ
Pageで画面遷移するには
• ViewModel側では以下のようにコンストラクタでINavigationServiceを受け取り、それを使って画面遷移をする
Modelはどうするの?
• Appクラスで作成してViewModelに渡すのがいいと思います
ReactiveProperty
ReactiveProperty
• Reactive Extensions(Rx)をベースにしたMVVMサポートライブラリ
• V ⇔ ViewModel⇔ Modelの間をRxでシームレスに繋ぐのが特徴
ReactiveProperty
•以下のクラスを提供• ReactiveProperty<T> class :ViewModelのプロパティ用
• ReactiveCommand class :ViewModelのCommand用
• ReadOnlyReactiveCollection<T> class:ViewModelのコレクション用
• その他拡張メソッドを提供• INotifyPropertyChangedの拡張メソッド
• ToReactivePropertyAsSynchronized method :MのプロパティとVMのReactivePropertyの同期をとるためのメソッド
• ObservableCollection<T>の拡張メソッド• ToReadOnlyReactiveCollection method :MのObservableCollection<T>と
VMのReadOnlyReactiveCollection<T>の同期をとるためのメソッド
ReactiveProperty class
• ViewModelに定義するプロパティ• public ReactiveProperty<string> Name { get; private set; }のように定義
• コンストラクタで初期化• this.Name = new ReactiveProperty<string>(); // nullを持った状態
• this.Name = new ReactiveProperty<string>(“なまえ”); // 初期値を持った状態
• this.Name = source.ToReactiveProperty(); // IObservable<T>から変換
• this.Name = person.ToReactivePropertyAsSynchronized(x => x.Name); // Modelのプロパティから変換
ReactiveProperty class
• IObservable<T>からの変換• 超便利なので覚えておくべき
• model // 何かINotifyPropertyChangedの実装クラス.PropertyChangedAsObservable(x => x.Name) // IObservable<T>に変換.ToReactiveProperty(); // ReactivePropertyに変換
• ↑でModelからViewModelへの一方通行値同期が可能
ReactiveProperty class
• ToReactivePropertyAsSynchronized methodも便利• model // INotifyPropertyChangedの実装クラス
.ToReactivePropertyAsSynchronized(x => x.Name); //Nameプロパティと同期
• model // INotifyPropertyChangedの実装クラス.ToReactivePropertyAsSynchronized(
x => x.Age, // Ageプロパティをx => x.ToString(), // M -> VMの時は文字列に変換してx => int.Parse(x)); // VM -> Mの時はintに変換する
ReactiveProperty class
• ToReactivePropertyAsSynchronized methodも便利• model // INotifyPropertyChangedの実装クラス
.ToReactivePropertyAsSynchronized(x => x.Age, // Ageプロパティをx => x.Age.ToString(), // M -> VMの時は文字列に変換してx => int.Parse(x), // VM -> Mの時はintに変換するignoreValidationErrorValue: true); // 変な値は通さない(次ページ参照)
ReactiveProperty class
•値の検証をサポート• // 何らかの方法でRPを作成
this.Hoge = xxxx.ToReactivePropertyAsSynchronized(…)// 検証ロジックを設定(nullの時はOKでそうじゃないときはエラーメッセージ).SetValidateNotifyError(x =>{
var x = 0;if (int.TryParse(x, out x)) { return null; }else { return "整数を入力してください"; }
});
ReactiveCommand class
• IObservable<bool>から生成可能なCommand• public ReactiveCommand HogeCommand { get; private set; }
• コンストラクタで以下のように初期化• this.HogeCommand = new ReactiveCommand(); // 常に実行可能なコマンド
• this.HogeCommand = source.ToReactiveCommand(); // IO<bool>がtrueの時だけ実行可能なコマンド
ReactiveCommand class
• コマンドの処理はSubscribeメソッドでやる• this.HogeCommand.Subscribe(_ =>
{// コマンド実行時の処理
});
ReadOnlyReactiveCollection class
• ModelのObservableCollection<T>をViewModelで読み取り専用コレクションとして公開する
•以下のように定義する• public ReadOnlyReactiveCollection<PersonViewModel> People { get;
private set; }
•以下のように初期化する• // modelでObservableCollection<Person>が定義されてるとする
this.People = model.People// model.Peopleと同期をとったPersonViewModelのコレクションを作成.ToReadOnlyReactiveCollection(x => new PersonViewModel(x));
組み合わせて使うPrismとReactiveProperty
基本はModelは普通に書く
• INotifyPropertyChangedとObservableCollection<T>による変更通知
•機能をメソッドとして提供
ViewModelをPrism + ReactivePropertyで
• ViewModel classを継承
• ReactiveProperty<T>でプロパティを定義
• OnNavigatedToメソッドでReactiveProperty<T>の初期化
• OnNavigatedFromメソッドで後始末
後始末
• ReactivePropertyとModelを接続したら最後に接続を切ること
• じゃないと予期せぬ処理が走ったりする
•具体的には• OnNavigatedToでCompositeDisposableに集める
• OnNavigatedFromでDisposeする
後始末
• こんな感じ• private CompositeDisposable disposable = new CompositeDisposable();
• OnNavigatedToで• this.Name = hoge.ToReactiveProperty().AddTo(this.disposable);
• this.People = model.People.ToReadOnlyReactiveCollection(x => new PersonViewModel(x)).AddTo(this.disposable);
• OnNavigatedFromで• this.disposable.Dispose();
まとめ
Prism
• BindableBase
• ViewModelLocator
• ViewModel
• MvvmAppBase(NavigationService, SessionStateService)• OnInitializeAsync
• OnLaunchApplicationAsync
ReactiveProperty
• ReactiveProperty<T>
• ReactiveCommand
• ReadOnlyReactiveCollection<T>
• INotifyPropertyChanged extension method• ToReactivePropertyAsSynchronized
• ObservableCollection<T> extension method• ToReadOnlyReactiveCollection
まとめ
• あとはケーススタディの積み重ね