Silverlight を使ったLarge-Scale Applicationの構築- MVVM及び Prism のご紹介 - 鈴木 章太郎アーキテクトエバンジェリストマイクロソフト株式会社http://blogs.msdn.com/shosuz/ Twitter : @shosuz
アウトラインMVVMModel – View – ViewModel何をするものかどこで使えば一番うまく使えるか応用PrismMVVMを有効に使うためにギャップを埋めるためにオプション
ゴールSilverlight におけるMVVM の重要性ゴールを達成するためには多くの手段がある実際に動かしてみようMVVM イネーブラについて学ぼうPrism独自のものを作成しよう
他にも MV_ とついているものが…?MVVMMVCMVPMV????アプリケーション構築において、メンテナンス性やスケーラビリティ確保のために、一般的に受け入れられているパターン
Model-View-ViewModel全体像MVVM, Prism, MEF - WCF RIA Services連携WPF、Silverlight の疎結合 ソリューションのパターン下記の実装を行う (トライアド)ModelData(Web)Service のエンティティ(をラップする)WCF RIA ServicesをModelと することも可能ViewModelModelを UI に合わせて公開するViewViewModelをXAML等でバインドする参照:http://msdn.microsoft.com/ja-jp/magazine/dd458800.aspx
MVVMどんなものか?なぜ必要か?関心の分離View = UI を担当Model = 純粋なデータを含むViewModel = バインディングを通じたView と Model との間のコミュニケーションSilverlight と WPF とで有効活用可能XAML ベースのデータバインディングテスト可能
MVVMどんなものか?なぜ必要か?Model :INotifyPropertyChanged、IDataErrorInfoを実装しているクラス (UI要素は持たず、Setter/Getterとしてのプロパティを持つのみ)ViewModelが取り扱うデータを保持するものViewModel:INotifyPropertyChangedを実装しているクラス (UI要素は持たず、ビジネスロジックやサーバーとの通信を担当する)多くはObservableCollectionとしてViewにバインドするデータ(Model)を取得し、保持するもの分離コードでCommandとイベントハンドラ(Command Receiver)の対応付けを行うExpression Blendのオブジェクトデータソースとしてスタティックリソース宣言が可能(ドラッグ&ドロップでUI構築) View:XAMLによるUserControl (UI要素のみ)ViewModelから提供されるデータ(Model)を2-waysデータバインドにより表示・更新するXAML側でCommandの宣言を行うできれば分離コードを一切もたないdumbコントロールとしての実装が望ましい(ViewModelの割り当てをViewの分離コードで実行することもできるが、View –ViewModelの分離が不明確になる可能性がある)
The MVVM のトライアドすべてを一緒にViewコントロールにデータを表示UI フレンドリーなエンティティや、UI 状態、アクションデータを表すエンティティViewModelModel
ModelMVVMデータを表すエンティティどこから取得したデータかは知る必要なしWCF サービス、WCF RIA Services 等々バリデーションを含む場合あり
ViewMVVM画面, UI, SilverlightのUserControlUI のルック&フィールを担当情報を表示バインディングを通じて、ViewModelとコミュニケート
ViewModelMVVMMVVM トライアドのためのロジックのメインソースModel をView と接続するViewを抽象化するView にバインドされたPublic プロパティINotifyPropertyChangedと、INotifyCollectionChangedとによる、バインディングを通じた View との会話バインディングを通じたView からの変更の通知MVVMトライアドの外部とのコミュニケートのためのサービスの呼び出し
MVVMはどこから始めるのが良いか?MVVM バリエーションView は ViewModelと何らかの関連性を持つ幾つかの実施可能なオプションView FirstView Model FirstMVVM トライアドの個々のパーツのメンテナンス
View First MVVM VariationsViewModelは View の XAMLの中のスタティックなリソースとして宣言されるExpression Blend で作れるもう一つの方法としては、ViewModelをView の分離コードで作成する
View First MVVMdemo
ViewModel FirstMVVM バリエーションView は、ViewModelのコンストラクタにインジェクト(=注入)されるExample:View がDependency Injection を使って作成されると、ViewModelが 作成される
View と ViewModelとの結び付けMVVM バリエーションView は ViewModelと何らかの形で一対になるViewModelと View は一旦作成され、中間段階を経て、その後一対となる
View とViewModelとの結び付けvm = new MyVM();view = new MyView();view.DataContext = vm;// With Unityvm = container.Resolve<IMyVM>();view = container.Resolve<MyView>();view.DataContext = vm;
Prism についていつでも選択可能Prism は、オプションのセット使いたいものだけを選んで使い、残りは無視して良し例 : モジュールとコマンドは選ぶが、イベントアグリゲーションとリージョンは無視するPrism: patterns & practices Composite Application Guidance for WPF and Silverlight sitehttp://www.codeplex.com/CompositeWPF
Prism の技術コンセプトCommandsShellModulesBootstrapperEvent AggregationContainerRegionsUnity and DI
Bootstrapperスタートするための機能アプリケーションを起動メイン UI コンテナをスタート (Shell)(必要に応じて)モジュールの登録とロードグローバルシングルトンの登録 (オプション)通常 “Bootstrapperプロジェクト” に含まれる
ShellメインViewメイン UI コンテナロードされ得るすべての Viewをホストリージョンに分割可能Shell 自身は、その中に何がロードされるかを知らない
RegionsContent エリアView を配置できる Shell の中のエリア名前を付ける必要ありコンテキストを内包可能 (オプション)RegionManagerによりRegionをメンテナンス可能
ModularitySelf Contained Modulesユーザーにとってはシームレス分割して開発される他のモジュールを参照してはならないSolution はModules に分割されるExample:City government applicationModule 1: Managing land parcelsModule 2: Traffic light administrationModule 4: City Parks Modules は、infrastructure と Models をシェアする
Dependency InjectionUnityの利用Unity 又は他の DI ツール群 (例:Ninject)テスト可能でモック作成抽象化コンテナオブジェクトにより、クラスがそのインターフェースに従って登録されるインターフェースをリクエストされたとき、コンテナは、当該インターフェースと一緒に登録されたクラスを、作成するシングルトンのサポート
クラスへのインターフェースの登録container.RegisterType	<IMyViewModel, MyViewModel>();
コンクリートClass の作成container.Resolve<IMyViewModel>();
Infrastructure共通のToolsSilverlight クラスライブラリプロジェクトモジュールのために共有可能なアイテムを含むClassesAssetsResources何の参照も作成しない純粋なライブラリ
Commandingアクションとリアクションデータバインディングを通じた View とViewModelとの間のイベントを許可ViewModelが宣言するのは Command receiverCommand は XAMLの中で宣言されるButton - ClickListBox (Selector) – SelectedCommand は、ViewModel の Command receiver にデータバインドされるルールベースで disabled/enabled 設定可能
Commanding 実装のための5つのステップ – 前提Silverlight 4 プロジェクトにおけるICommand実装は、数ステップで足りる問い合わせの多い個所なので、下記にCommanding 実装のためのシンプルなテクニックを記述する
Commanding 実装のための5つのステップ – ICommandの実装最初のステップは、ICommandインターフェースクラスの実装このクラスはCommanding アスペクトを管理する他にも色々オプションがあるところ、ここではシンプルなICommand の実装方法を紹介当該 DelegatedCommandクラスで実装するのは、ICommandのCanExecuteメソッド、Execute メソッド、そして、CaneExecuteChangedイベントこのコードはそのままコピー&ペーストして利用可能
Commanding 実装のための5つのステップ – ICommandの実装 ① public class DelegateCommand : ICommand {Func<object, bool> canExecute;	Action<object> executeAction;boolcanExecuteCache;	public DelegateCommand(Action<object> executeAction, Func<object, bool>canExecute)	{this.executeAction= executeAction;this.canExecute= canExecute;	}…
Commanding 実装のための5つのステップ – ICommandの実装 ②#region ICommandMembers	public boolCanExecute(object parameter)	{booltemp = canExecute(parameter);		if (canExecuteCache != temp)		{canExecuteCache= temp;			if (CanExecuteChanged != null)			{CanExecuteChanged(this, new EventArgs());			}		}		return canExecuteCache;	}
Commanding 実装のための5つのステップ – ICommandの実装 ②・・・続き・・・	public event EventHandlerCanExecuteChanged;	public void Execute(object parameter)	{executeAction(parameter);	}#endregion}
Commanding 実装のための5つのステップ – Commandの定義//ICommand を表示するための Public プロパティを ViewModelに追加//このプロパティは 、通常は、ボタン等により、  View にバインドされるpublic ICommandLoadProductsCommand{ get; set; }
Commanding 実装のための5つのステップ – Command の作成//ViewModelのコンストラクタの中で、 1で作成したコマンドプロパティを設定するLoadProductsCommand=  new DelegateCommand (LoadProducts, CanLoadProducts);
Commanding 実装のための5つのステップ – ViewModelの作成//ViewModelが View からアクセス可能なこと を確認する// 様々な方法があるところ、シンプルに View の XAML 中の Static Resource として作成<UserControl.Resources> 	<local:ProductViewModel									x:Key="vm"/></UserControl.Resources>
Commanding 実装のための5つのステップ – Command のバインド//ボタンコントロールを追加し、当該コマンドプロパティを ViewModelの中で作ったコマンドにバインドする//当該Command に parameter を渡したい場合には、 CommandParameterプロパティを、当該 View の中の要素にバインドできる (通常は parameter は必要ないが、例として実装) <Button Content="Load" Width="120" Command="{Binding LoadProductsCommand}" CommandParameter= 			"{Binding ElementName=FilterTextBox, Path=Text}" />
Commanding 実装のための5つのステップ – ProductViewModel完成品public class ProductViewModel : ViewModelBase{ public ProductViewModel(){this.Products= new ObservableCollection<Product>(); this.AllProducts= new ObservableCollection<Product>();this.AllProducts.Add(new Product { ProductId = 1, ProductName = "Apple" });this.AllProducts.Add(new Product { ProductId = 2, ProductName = "Orange" });this.AllProducts.Add(new Product { ProductId = 3, ProductName = "Banana" }); this.AllProducts.Add(new Product { ProductId = 4, ProductName = "Pear" })this.AllProducts.Add(new Product { ProductId = 5, ProductName = "Grape" }); this.AllProducts.Add(new Product { ProductId = 6, ProductName = "Grapefruit" });this.AllProducts.Add(new Product { ProductId = 7, ProductName = "Strawberry" })this.AllProducts.Add(new Product { ProductId = 8, ProductName = "Melon" });this.AllProducts.Add(new Product { ProductId = 9, ProductName = "Guava" });this.AllProducts.Add(new Product { ProductId = 10, ProductName = "Kiwi" });this.AllProducts.Add(new Product { ProductId = 11, ProductName = "Pineapple" });this.AllProducts.Add(new Product { ProductId = 12, ProductName = "Mango" });LoadProductsCommand= new DelegateCommand(LoadProducts, CanLoadProducts); 	} 	private void LoadProducts(object param) {	string filter = param as string ?? string.Empty; this.Products.Clear();varquery = from p in this.AllProducts	where p.ProductName.ToLower().StartsWith(filter.ToLower())	select p;foreach(var item in query) { this.Products.Add(item);	}} 	private boolCanLoadProducts(object param) {	return true } 	public ICommandLoadProductsCommand { get; set; } public ObservableCollection<Product> AllProducts { get; set; } private ObservableCollection<Product> products; 	public ObservableCollection<Product> Products { 		get		{ 				return products		}		set		{	products = value;this.FirePropertyChanged("Product");		} } }
Commanding 実装のための5つのステップ –ViewModel Basepublic abstract class ViewModelBase : INotifyPropertyChanged{public ViewModelBase(){}public event PropertyChangedEventHandlerPropertyChanged; 	protected void FirePropertyChanged(string propertyname) {varhandler = PropertyChanged; 		if (handler != null) 				handler (this, new PropertyChangedEventArgs(propertyname));}}
Event Aggregatorパブリッシャとサブスクライバ色々な種類のイベントをパブリッシュ及びサブスクライブ可能にするクロスモジュール化可能サブスクライバによるフィルタリング可能例えば:Shell の中のMenu アイテム上をクリックイベントはパブリッシャから呼び出されるイベントはサブスクライバに受信されるそれにより、当該サブスクライバは、 Shell の中の、ある Region の中の、一つのViewをロードする
Infrastructure共通のToolsInfrastructure は、Silverlight class library プロジェクトクラス、アセット、リソースを含み、モジュール間で共有される他のモジュールを参照してはならないWeb サービスをコールしてはならない純粋なライブラリ
Modular MVVM and Prismdemo
Screen Presentation EnablerScreensを生成するスクリーン間をナビゲート可能にしなければならないView 同志はお互いのことを知らない独自のScreen Conductor を作成可能Loading / UnloadingDisplaying / Hidingそのまま終了して良いですか?自分でクリーンアップしましょう!
Screen Presentation FrameworkKey PlayersScreenMVVM トライアドを管理ScreenFactoryScreen class を作成ScreenFactoryRegistryScreenFactoryディレクトリScreenConductorScreen のアクティベーションイベントをリッスンしたり、その上で動いたりするScreenCollectionScreensを収集する
Screen Conductordemo
参考情報その他Blog、チュートリアル、サンプル、等々Introducing the Earthquake Locator – A Bing Maps Silverlight Application, part 1 http://geekswithblogs.net/bdiaz/archive/2010/03/06/introducing-the-earthquake-locator--a-bing-maps-silverlight-application.aspx MVVM Light Toolkithttp://www.galasoft.ch/mvvm/getstarted/Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 25: ViewModelhttp://blogs.msdn.com/brada/archive/2009/09/07/business-apps-example-for-silverlight-3-rtm-and-net-ria-services-july-update-viewmodel.aspxViewModel Pattern in Silverlight using Behaviorshttp://www.nikhilk.net/Silverlight-ViewModel-Pattern.aspxSimple Step for Commanding in Silverlight http://devlicio.us/blogs/christopher_bennage/archive/2010/03/03/1-simple-step-for-commanding-in-silverlight.aspx
Building Silverlight Large Scale Application Using MVVM

Building Silverlight Large Scale Application Using MVVM

Editor's Notes

  • #9 悩ましいのは、View上のいろんなイベント処理をいかに分離コード無しでコントロールするか、という点理想的にはViewはBehaviorを駆使して・・・、と行きたいところだが、業務系のシナリオを考えると難しい?スライド27 Commanding、28にあるEvent Aggregator、このあたりの明確な指針を出したい
  • #39  // Warning: DEMO CODE AHEAD // Your ViewModel should not define your data for your Model :-) // Instead, it could make a call to a service to get the data for the Model.
  • #40  // Warning: DEMO CODE AHEAD // Your ViewModel should not define your data for your Model :-) // Instead, it could make a call to a service to get the data for the Model.