WPF MVVM Infrastructure Livet

+15

No comments posted yet

Comments

Slide 1

RIAアーキテクチャ研究会 第4回セミナー 尾上 雅則

Slide 2

自己紹介 尾上 雅則 (おのうえ まさのり) 昭和58年生。フリーランス Blog – the sea of fertility http://ugaya40.net Twitter - @ugaya40 Facebook – http://facebook.com/ugaya40 C#er/MVVMer Microsot MVP for Visual C#(2012/7~)

Slide 3

Agenda WPF MVVM開発の悩みどころ Livetの概要 Livetのライフサイクル管理 冗長で面倒なコードを倒す Livetのメッセージング機能 Livetのその他の機能 まとめ - Livetの未来

Slide 4

WPF MVVM開発の悩み処

Slide 5

MVVM という考え方 – 概要 Model – View – ViewModelパターン WPFをはじめとするXAML系プラットフォームのために生まれたMVC系パターン PresentationDomainSeparationを目的とする 現在はXAML系のみならずAndroidやブラウザJavascriptの世界でも利用される MVCなどと比べて優劣があるものではなく、適するプラットフォームと適さないプラットフォームがあるのみであることに注意

Slide 6

MVVM という考え方 – 各責務 Viewの責務 XAMLとXAML関連コードで賄える事(表示など) ViewModelの責務 XAMLのための状態ストア Modelの責務 ViewとViewModel以外の部分(XAMLプラットフォームが関連ない部分) 詳細はこちら http://ugaya40.net/architecture/20120804wankuma.html

Slide 7

問題① – メモリリーク MVVMに限らず、Observaerパターンを使用するステートフルなリッチクライアントのための責務分割型設計パターンではWebシステムでは発生しにくいイベント関連のメモリリークが非常に発生しやすい。 .NETにおけるイベントの仕組みと参照の関係 責務分割した際のライフサイクル C#でのイベントハンドリングと解除

Slide 8

イベントの仕組み - 問題① || .NETではイベント発行側がイベント受信側の参照を保持している。つまりイベント受信側はイベントの購読を解除してからじゃないと勝手に消滅できない。 イベントの購読を解除せずに、受信側オブジェクトを破棄したつもりだと実際には受信側オブジェクトは消滅せずメモリリークとなる。

Slide 9

責務分割型のライフサイクル – 問題① ViewとViewModelは基本データバインド(内部でWeakEventが使用されメモリリークしない) Model(ViewとViewModel以外の部分)はアプリケーションの本質にかかわる部分なのでずっと消滅しないことが多い。つまりViewModelより長生き。

Slide 10

責務分割型のライフサイクル – 問題① < 長生き イベント監視 参照保持 イベントハンドラの解除をかなり正確にやらないとリスキーなのはわかりますよね?

Slide 11

C#でのイベント - 問題① C#でのイベントハンドリング、どう書きますか? では解除はこうですか? これでは解除できません。残念ながらラムダで直接イベントハンドラを書いた時点でイベントを解除する手段はなくなります。

Slide 12

C#でのイベント - 問題① イベントハンドリングとその解除を適切に行うにはこうします。 しかしイベントハンドラの登録と解除を同じメソッド内で行うことは稀有です。つまり・・

Slide 13

C#でのイベント - 問題① ハンドラとイベント発行オブジェクトでフィールド管理が地獄に!!!

Slide 14

問題① – メモリリーク まとめ MVVMパターンではイベント受信側オブジェクトの方が基本的に寿命が短く、メモリリークのリスクが大きい。 そのため適切にイベントハンドラの登録と解除を管理する必要がある。 C#でのイベントハンドラの登録と解除が複数メソッドにまたがる場合(その方が多い)、クラスフィールドでイベントハンドリング関連オブジェクトが増えやすくつらい。

Slide 15

問題②–バインドできないプロパティ MVVMパターンのViewModelはViewの状態ストアです。XAMLはバインディングファーストなDSLであるため、Viewは基本的にViewModelのプロパティをバインドで表示する形でレンダリングされます。 ViewとViewModel間の通信は極力データバインディングで統一されるほど問題が発生しにくくなっています。 データバインディングを使用するとViewはViewModelの変更通知イベントを監視しますが、データバインディング機構内部ではWeakEvent(弱いイベント)という機構が使用されているため、メモリリークが発生しません。 単純に責務感通信方法が統一されるほど管理は容易

Slide 16

問題②–バインドできないプロパティ バインドできない! 依存関係プロパティとして用意されているもの以外はバインドできません。それが結構多いんです・・。

Slide 17

問題③–冗長なコード1 バインドするために必須な変更通知プロパティ。 冗長なコードの代表格ですね。面倒です。(ViewModel & Model)

Slide 18

問題③–冗長なコード2 操作をバインドするためのICommand実装。これも冗長です。(ViewModel)

Slide 19

WPF MVVM開発の悩み処 – まとめ メモリリークが発生しやすい & きちんとした管理が難しいのでイライラする バインドできないプロパティにイライラする 冗長なコードにイライラする → 要はイライラする。 ※一番上のもの以外はMVVMのせいではなくWPF(というかXAML系全般)自体の問題であることに注意 Livetはこれらに対応するソリューションです。

Slide 20

Livetの概要

Slide 21

Livetとは? WPF4のための「国産」MVVMインフラストラクチャ ライセンスはzlib/libpng 通常の使用であれば、クレジットなど必要ない ソース改変使用する場合はクレジットの明記が必要 Project Home http://ugaya40.net/Livet GitHub https://github.com/ugaya40/Livet

Slide 22

Livetとは? 来月(2012/10)で開発開始から2年 採用実績は企業・個人開発者ともに多数 おそらくは日本一のC#erたちが集うTLにて多くの方からフィードバックをいただき、議論を積み重ねて作ってきました MVVMの概念そのものを突き詰めた結果のシナリオをベースに機能を構築しており、ほかのMVVMライブラリが未着手な問題の多くに着手できています

Slide 23

Livetのインストール Visual Studio 2010/2012なら拡張機能マネージャから一発導入 Visual Studio 2010 Express C#/Visual Basic あるいはVisual Studio 2012 Express for Windows DesktopならSetup.exeから一発導入 ライブラリだけならNugetからも導入可能です

Slide 24

Livetの構成 Visual Studio2010/2012/Express各環境用、C#/VisualBasic双方用のプロジェクトテンプレート・アイテムテンプレート・コードスニペットなどが含まれます。

Slide 25

Livetのライフサイクル管理

Slide 26

Livetのライフサイクル管理 Livet0.99系までは、Model⇔ViewModel間のライフサイクル管理にWeakEventを使用してメモリリークを防いでいました。 しかし、WeakEventはCPUが回りすぎるというフィードバックをいくつかいただいたため、またViewModelとModelの間はView⇔ViewModelの間ほどライフサイクル管理が難しくはないため、現在のLivet1.0系ではすべて手動管理する形になっています。 ※View⇔ViewModel間は極力データバインディングによる通信を行う事で、WeakEventが自動的に使用されます

Slide 27

Livetのライフサイクル管理 メモリリークの問題、このコードをLivetはどう変えるのでしょうか?

Slide 28

Livetのライフサイクル管理 ViewModelではこうなります。ともにLivetの機能であるPropertyChangedEventListenerとCompositeDisposableが鍵です。

Slide 29

PropertyChangedEventListener PropertyChangedEventListenerとは、PropertyChangedイベント購読状態をIDisposableとして扱うための機能です。Disposeによってイベント購読が解除されます。 また、普通にイベントハンドラでは変更通知が来たプロパティ名ごとにこうなりがちなところが・・

Slide 30

PropertyChangedEventListener あるいはコレクション初期化子を用いて・・ こう書けたりします。

Slide 31

LivetCompositeDisposable LivetのViewModel型には最初からメンバとして定義されているIDisposableのコレクションです。CompositeDisposableをDisposeすることでコレクションメンバのIDisposableもすべてDisposeされます。 LivetのViewModel(IDisposable)型ではViewModelがDisposeされる際にCompositeDisposableがDisposeされます。

Slide 32

LivetCompositeDisposable つまりこう記述するだけで、listenerのイベント購読もViewModel破棄時にすべてDisposeされるわけです。

Slide 33

ViewModelのDisposeは誰が呼ぶのか? LivetのWindowテンプレートではWindowを閉じる際に、DataContextがIDisposableであればDisposeを行うビヘイビアが最初から記述されています。 WPFのViewModelのオブジェクト階層は結局常にWindowのDataContextがルートにいます。メンバに子ViewModelが存在するViewModelも、その子ViewModelを自身のCompositeDisposableに放り込むことで簡単にハンドラの解除が管理できるわけです。

Slide 34

ViewModelのDisposeは誰が呼ぶのか? Windowが閉じられるとすべてのイベントハンドラや破棄可能なリソースが連鎖的に破棄されます

Slide 35

Livet - ViewModelのライフサイクル管理 LivetではPropertyChangedEventListener以外にも、CollectonChangedEventListener、ユーザー定義イベントに対応するための汎用クラスEventListenerクラスが定義されています。 LivetのViewModelでは用意されているCompositeDisposableや各種EventListenerを使用してViewModelのライフサイクル管理が容易に行えるようになっています。

Slide 36

ViewとViewModelのライフサイクル管理 データバインドでの通信はWeakEvent機構が使用され、メモリリークは発生しえませんが、データバインドでは処理できないイベントが残ります。 次章で説明するメッセージング機構を使えば、ViewModelからのイベントをデータバインディングで扱えますが、ビヘイビア・トリガー・アクションなどの知識も必要ですし、そういったコードビハインドに依存しない実装はView抽象化要件がなければ意義は薄いものです。

Slide 37

ViewとViewModelのライフサイクル管理 Model⇔ViewModel間と異なりView⇔ViewModel間のハンドラの管理は難易度が高いため(Viewの生成消滅の頻繁さ・WPF自体バグ起因・部品コンポーネント化による複雑さ)自分でハンドラの管理をしっかりできれば一番ですが、不安な場合はLivetWeakEventListenerを使用することができます。 LivetWeakEventListenerは汎用WeakEvent機構です。.NET標準のものより手軽に、しかもはるかに高速に(実測4~5倍)WeakEvent機構を使用できます。

Slide 38

LivetWeakEventListener

Slide 39

冗長で面倒なコードを倒す

Slide 40

コードスニペット - デモ Livetでは変更通知プロパティ・ICommand2種のコードスニペットが用意されています。 ReSharper7のINotifyPropertyChangedサポートにも対応しています。 lprop/lpropn それぞれ.NET4/4.5用の変更通知プロパティ lvcom/lvcomn 引数を取らないICommand実装クラスViewModelCommandのフル・CanExecute無し版 llcom/llcomn 引数を取るICommand実装クラスListenerCommandのフル・CanExecute無し版

Slide 41

メソッド直接バインディング - デモ Livetを使用すると実はICommandを全く使わずにViewModelからViewに操作を公開することも可能です。 LivetのView機能はすべてICommand無しのメソッド直接バインディングに対応しています 内部ではリフレクション・式木・Taskなどをフル活用したメソッドキャッシュ機構があり、またそれらメソッドキャッシュ生成機構はMethodBinderというクラスにまとめてあり、開発者が自身のクラスに簡単にメソッド直接バインディングの仕組みを追加することもできます。

Slide 42

メソッド直接バインディング LivetCallMethodActionはBlend SDKのCallMethodAcitonより高速で、かつ引数をとることもできます。 後述するメッセージング機構でもメソッド直接バインディングは使用できるので後でまた確認することにします。

Slide 43

Livetのメッセージング機能

Slide 44

メッセージング機構とは? ViewModelからViewに操作指示メッセージを送るための機構です。 ViewModelから既定のメンバであるMessengerを使用してViewにメッセージを送信します。 現在のLivetでは、XAML上に記述するInteractionMessageTriggerでMessengerからのメッセージを受け、対応する処理となるアクションを実行します。 View抽象化要件がなければ、しいてメッセージング機構に頼る必要はありません!

Slide 45

2種類のメッセージ対応アクションの起動-デモ ViewModelからInteractionMessageTriggerを介しての起動 ViewModelのMessenger + InteractionMessageTriggerによるアクションの起動 Viewから直接の起動 EventTriggerなどの任意のトリガーによるアクションの起動

Slide 46

Livetのメッセージング機能まとめ ご覧のとおり、Livetのメッセージ機能はすべてメソッド直接バインディングが使用でき、かつView起点・ViewModel起点でアクションを起動できます。 メッセージング機構はイベントをデータバインディングで処理するような機構であり、その内部ではデータバインディングと同じくWeakEventが使われています。 View抽象化要件がないシナリオでは特にこだわる必要はありません。

Slide 47

そのほかのLivet機能

Slide 48

ViewModelHelper. CreateReadOnlyDispatcherCollection Modelの変更通知コレクションに自動的に連動するViewModelのコレクションを作製します。 戻りであるReadOnlyDispatcherCollectionはIDisposableであり、DisposeすることでModelコレクションとの連動が解除され、自身の各要素がIDisposableである場合すべてDisposeします。 簡単にModelコレクションからViewModelのコレクションを生成でき、CompositeDisposableと組み合わせることで管理も容易です。

Slide 49

バインドできないプロパティ対策 Livetではバインドできないプロパティの単一方向バインドを可能にするビヘイビアとアクションが大量に含まれています。 バインドシステムの制約から、2種類ともつけて双方向というやり方はできません。

Slide 50

ビヘイビア・アクション・トリガーなど LivetDataTrigger Blend SDKのそれと違い、初期値に対応したDataTrigger WindowCloseCancelBehavior Windowのクローズ可否処理をViewModelなどバインド先に委譲できるビヘイビア SetFocusAction アタッチしたコントロールにフォーカスを移動する など

Slide 51

その他 ObservableSyncronizedCollection スレッドセーフなObservableCollection 汎用EnumToBooleanConverter Enumとboolの相互変換を行いIValueConveterの実相の手間を削減する アタッチしたコントロールにフォーカスを移動する など

Slide 52

Livetの未来 - まとめ

Slide 53

Livetの今後の予定 Livet英語化プロジェクト コードビハインド実装の補完 より簡潔なViewModelイベントのコードビハインドでのハンドリングなど バインドできないプロパティ対策の高度化 バインドできないプロパティの中からシナリオベースでよく使われる機能を双方向バインド可能にする (すでにTextBoxとPasswordBoxはある)

Slide 54

Livet - まとめ LivetはMVVMについて適切に理解していれば適切に理解しているほどその機能がいかせるライブラリです。 ほかのMVVMライブラリと異なりViewModel単体ではなく全体を見たライブラリになっています。 プロジェクトテンプレート・スニペット・ハンドラ管理・メソッド直接バインディングなどの機能をつかっていただければ2度とほかのMVVMライブラリには戻れないものになっていると思います。

Slide 55

ご清聴ありがとうございまいた!

Summary: Livet is MVVM infrastructure for WPF4/4.5 by Japanese MVVMers.

Tags: mvvm livet

URL: