<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-24166670934445279</id><updated>2025-05-19T17:23:28.154+09:00</updated><category term="設計"/><category term="mvvm"/><category term="viewmodel"/><category term="wpf"/><category term="silverlight"/><category term="原則"/><category term="AWS"/><category term="BI"/><category term="Datamart"/><category term="Datawarehouse"/><category term="ELT"/><category term="ETL"/><category term="Redshift"/><category term="messenger"/><category term="dialog"/><category term="presentation model"/><category term="メッセンジャー"/><category term="Behavior"/><category term="MVVM Dialog Behavior"/><category term="library"/><category term="navigation"/><category term="transition"/><category term="ダイアログ"/><category term="画面遷移"/><title type='text'>トロこんぶ</title><subtitle type='html'>Javaや.NETの技術について、徒然語ります。</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://torokonbu.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-7165170522786241668</id><published>2018-06-08T15:47:00.001+09:00</published><updated>2020-02-04T10:15:05.509+09:00</updated><title type='text'> なぜ今Apache Camelを学ぶ必要があるのか？</title><content type='html'>&lt;h3&gt;
なぜ今 Apache Camel を学ぶ必要があるのか?&lt;/h3&gt;
- なぜ今Apache Camelを学ぶ必要があるのか？ -&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;a href=&quot;https://jcug-oss.github.io/article/why-do-we-have-to-learn-camel-q&quot;&gt;https://jcug-oss.github.io/article/why-do-we-have-to-learn-camel-q&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
現在、私はRed Hat社にて Red Hat FuseとRed Hat AMQという製品のサポートをしているのですが、Red Hat Fuse のコアコンポーネントは Apache Camelになっています。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
このApache Camelは既に10年以上の歴史を持つのですが、&lt;a href=&quot;https://jcug-oss.github.io/article/why-do-we-have-to-learn-camel-q&quot;&gt;なぜ今Apache Camelを学ぶ必要があるのか&lt;/a&gt;、大まかな機能とアーキテクチャから説明した記事を書きました。読むのに多分10分くらいかかるのですが興味ある方はご覧ください。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
また、月１回のペースで&lt;a href=&quot;https://jcug-oss.connpass.com/&quot;&gt;Camel In Action&lt;/a&gt;&lt;a href=&quot;https://jcug-oss.connpass.com/&quot;&gt;読書会&lt;/a&gt;を行っていますので、興味ある方はご参加ください。&lt;br /&gt;
&lt;br /&gt;
- Japan Camel User Group&lt;br /&gt;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;a href=&quot;https://jcug-oss.connpass.com/&quot;&gt;https://jcug-oss.connpass.com&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://jcug-oss.github.io/assets/images/camel-blue.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;533&quot; data-original-width=&quot;800&quot; height=&quot;213&quot; src=&quot;https://jcug-oss.github.io/assets/images/camel-blue.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;h2 style=&quot;border: 0px; caret-color: rgb(68, 68, 68); color: #444444; font-family: &amp;quot;Lucida Grande&amp;quot;, Verdana, &amp;quot;ヒラギノ角ゴ Pro W3&amp;quot;, &amp;quot;Hiragino Kaku Gothic Pro&amp;quot;, &amp;quot;MS Pゴシック&amp;quot;, &amp;quot;MS P Gothic&amp;quot;, Osaka, sans-serif; font-size: 2em; font-weight: normal; line-height: 1.4; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; word-wrap: break-word;&quot;&gt;
&lt;/h2&gt;
&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/7165170522786241668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/7165170522786241668'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2018/06/apache-camel.html' title=' なぜ今Apache Camelを学ぶ必要があるのか？'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-5038079867765604277</id><published>2016-12-11T23:55:00.001+09:00</published><updated>2016-12-27T10:29:30.951+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AWS"/><category scheme="http://www.blogger.com/atom/ns#" term="BI"/><category scheme="http://www.blogger.com/atom/ns#" term="Datamart"/><category scheme="http://www.blogger.com/atom/ns#" term="Datawarehouse"/><category scheme="http://www.blogger.com/atom/ns#" term="ELT"/><category scheme="http://www.blogger.com/atom/ns#" term="ETL"/><category scheme="http://www.blogger.com/atom/ns#" term="Redshift"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>Redshift設計での個人的知見のまとめ　その３</title><content type='html'>&lt;h3&gt;
データ収集とデータマート構築プログラム&lt;/h3&gt;
ここでは、データの源泉となるシステムからデータ収集を行い、Redshiftへとデータを配置してデータマートを構築を行う制御プログラムについて説明する。&lt;br /&gt;
&lt;br /&gt;
制御プログラムの実行内容：&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
①．データの源泉となるシステムからデータを取得し、CSVにしてS3に配置する&lt;br /&gt;
②．S3のCSVデータをRedshiftのステージングレイヤにロードする&lt;br /&gt;
③．ステージングレイヤのデータをオリジナルレイヤにアップサートする&lt;br /&gt;
④．オリジナルレイヤの有効期限を迎えたデータを除いてディープコピーを行う&lt;br /&gt;
⑤．モデルレイヤのテーブルを導出する&lt;br /&gt;
⑥．データマートレイヤのテーブルを導出する&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRQxPI7_Xg0t3EqaHQ9oKsR_XD46kZ8N35iEJ49R-1kMLWYGrGKGUC0eQdNkuau6XlBdlYJwn9WwsC65dwErvTzoBM2E9XUGvp5bEvRnRerUumzXgsjCQeauHTj4vL-t5hw6yFGDX7B14/s1600/ControlProgram.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;288&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRQxPI7_Xg0t3EqaHQ9oKsR_XD46kZ8N35iEJ49R-1kMLWYGrGKGUC0eQdNkuau6XlBdlYJwn9WwsC65dwErvTzoBM2E9XUGvp5bEvRnRerUumzXgsjCQeauHTj4vL-t5hw6yFGDX7B14/s640/ControlProgram.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
これらの実行内容をEC2インスタンスを深夜に定期的に起動して実行させていた。&lt;br /&gt;
&lt;h4&gt;
①．データの源泉となるシステムからデータを取得し、CSVにしてS3に配置する&lt;/h4&gt;
&lt;div&gt;
データの源泉となるのは主にデータベースだろう、データベース以外だとログファイルなどだろうか。RedshiftはS3上のCSVやJSONなどからデータロードすることができるが、私の場合には、データベースの内容をJDBCを通し、作成日時と更新日時を確認して必要な差分データを取得できるSQLを発行し、10000件ごとのGZIP圧縮されたCSVにフォーマット変換してローカルファイル出力しつつ、それを順次S3にアップした。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;10000件ごとにCSVファイルにするのはいくつか理由がある。まずデータをすべて取得する前にS3への転送を始められるために転送完了までの時間を短縮できること。それから制御プログラムのEC2インスタンスに滞留するデータが少なくなるために必要とされるメモリやディスク容量を少なく保てること。そして、Redshiftは複数のCSVファイルを指定してCopyすると並列ロードが行われてロード時間を短縮することができるからだ。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
②．S3のCSVデータをRedshiftのステージングレイヤにロードする&lt;/h4&gt;
&lt;/div&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;S3のCSVデータをRedshiftのステージングレイヤにロードするにはcopyコマンドを利用する。比較的高速に実行される処理で、差分データ量が1G程度と大きくなっても数十秒程度で完了する。&lt;/span&gt;&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;ところで、CSVと一口に言ってもそのフォーマットには様々な亜種が存在する。区切り文字は何か？文字列はダブルクォーテーションで囲むか？日付表現形式はどうするのか？さらにはファイルエンコードはUTF8でよいか？圧縮形式はGZIP圧縮か？。CSVのフォーマットの亜種が個別のシステムごとに存在すると保守しずらくなる。Redshiftで利用するCSVだけでなく、システム全体で必ず標準化しておこう。copyコマンドには様々なCSVの形式をロードするためのオプションがついているので標準化したCSV形式に合わせて調整する。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
③．ステージングレイヤのデータをオリジナルレイヤにアップサートする&lt;/h4&gt;
&lt;div&gt;
&lt;span style=&quot;background-color: white;&quot;&gt;ステージングレイヤにcopyコマンドで転送したデータを&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;オリジナルレイヤに&lt;/span&gt;&lt;span style=&quot;background-color: white;&quot;&gt;マージする。多くのテーブルには主キーが定義されているだろうから、ステージングレイヤとオリジナルレイヤのデータの主キーを比較すればそれが新しく挿入されたデータなのか更新されてデータなのかは判断できる。またRedshift内のテーブルの話なので、それぞれ新規データの差分を反映するSelect-Insertと、更新データ差分を反映するSelect-Updateを実行することでマージが実現できる。&lt;/span&gt;&lt;/div&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;実はこの①、②、③を１組として複数スレッドを使い、&lt;/i&gt;&lt;i&gt;データの源泉となるシステムやテーブルに対して&lt;/i&gt;&lt;i&gt;並列実行を行うことで転送時間を短縮することができる。ネットワーク帯域やRedshiftインスタンス種別によっても異なるだろうが、私のケースでは６〜８スレッド程度で同時に転送をすると最も転送時間を短縮することができた。最適なスレッド数は環境に依るのでいくらか試しながら数を調整していただければと思う。&lt;/i&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;また細かなテクニックではあるがデータ量の多いテーブルから並列にデータ転送を開始するようにするとクリティカルパスを短縮することができる。これは実行の最後の最後になってデータ量が多くて実行に時間がかかる処理が開始されて、それだけが単体実行される非効率な状態を防ぐことができるからだ。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
④．オリジナルレイヤの有効期限を迎えたデータを除いてディープコピーを行う&lt;/h4&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;オリジナルレイヤのデータは差分更新のためのマージinsertやupdateが行われる。そうすると未ソート領域にこれらのデータが溜まってクエリ速度が遅くなってしまう。こうした未ソート領域を開放するのがVacuumだ。だが、データ量が多くなった場合にはメモリを増やしても、&lt;/span&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;Vacuumで&lt;/span&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;エラーが発生してしまって実行させることができなくなるケースに遭遇した。それでVacuumを使うことは諦め、代わりとして利用したのがディープコピーと呼ばれる方法だ。要は全く同じスキーマの新しいテーブルを作ってそちらにSelect-Insertでデータを全件投入してしまうという方法だ。このときについでに有効期限切れたデータをコピー対象から外すことで、有効期限切れデータの削除の目的も達成できる。&lt;/span&gt;&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;i&gt;なお、有効期限切れのデータはRedshiftから削除する前に、S3上に念のため保存しておくと安心だ。unloadコマンドで簡単に任意のクエリ結果をS3上に保存することができる。もちろん&lt;/i&gt;&lt;/span&gt;&lt;i&gt;様々なCSV形式でエクスポートするためのオプションがあるので標準化したCSV形式に合わせて調整する。&lt;/i&gt;&lt;/blockquote&gt;
RedshiftのSelect-Insertはとても早い。しかしディープコピーはデータを全件移動することになるのでやはりそれなりに時間のかかる処理になる。一方、未ソート領域があってもクエリが動作しなくるわけでもないし、一般ユーザからのクエリの対象はデータマートのレイヤであってオリジナルレイヤへ求める速度の優先は全く高くない。それぞれシステムの性質にも依るだろうが数日、場合によっては１ヶ月程度であっても、放置による動作の低下は大きな問題にならないかもしれない。私の場合には、ディープコピーを行うだけの時間が日次で確保可能だったので毎日ディープコピーを行ったが、週次などの実行でも問題にならないシステムが多いのではないかと思う。それぞれの状況に応じてディープコピーの頻度は調整すると良い。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;ところで、この④からの処理は処理の主体がRedshiftに移り、Redshift自体に大きな負荷をかける。Redshiftは同時実行性能が低いので、これ以降の処理はシリアル実行としていた。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
⑤．モデルレイヤのテーブルを導出する&lt;/h4&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;これはオリジナルレイヤのデータからモデルレイヤのテーブルを導出する処理だ。だが、以前にも説明したようにモデルレイヤのテーブルはそのほとんどをビューとしていたので、実際にはほとんど処理がない。パフォーマンス上の理由から&lt;/span&gt;ごく一部だけをビューではなくテーブルとしており、そのテーブルだけを導出するにとどめていた。&lt;br /&gt;
&lt;h4&gt;
⑥．データマートレイヤのテーブルを導出する&lt;/h4&gt;
これはモデルレイヤのデータからデータマートレイヤのテーブルを導出する処理だ。モデルレイヤはほとんどをビューで構成していたので、物理的にはオリジナルレイヤのデータが使われるが、モデルレイヤにて各種データが正規化されていればデータマートレイヤのテーブル導出が簡単に行える。&lt;br /&gt;
&lt;br /&gt;
私の場合にはSelect-Insertを使ってテーブルのデータを全件を導出し直した。もちろんある程度時間はかかるが、RedshiftにとってSelect-Insertは得意な処理で、最も時間のかかるテーブルであっても20分程度で完了した。また全件導出によるディープコピーとなるので未ソート領域のないクエリに適したテーブルを導出できる。&lt;br /&gt;
&lt;h3&gt;
テーブル導出の処理ステップ&lt;/h3&gt;
&lt;div&gt;
テーブル導出する場合は、実はいくつかの処理ステップにて導出する必要がある。またディープコピーでもほぼ同様の処理ステップになる。&lt;/div&gt;
&lt;pre class=&quot;prettyprint language-sql&quot;&gt;-- １.新テーブルを作成する
create table a_table_new ( DDL... );

--&amp;nbsp;２.新テーブルに既存テーブルのデータを挿入する(有効期限内のもの)
insert into a_table_new (select * from a_table where 有効期限内...);

--&amp;nbsp;３.新しいテーブルと古いテーブルを入れ替える
start transaction;
alter table a_table rename to a_table_old;
alter table a_table_new rename to a_table;
commit;

--&amp;nbsp;４.新しいテーブルへ向き先が変わるようにビューを再定義する
create or replace view dependent_a_view as ( DDL... );
create or replace view dependent_b_view as ( DDL... );

--&amp;nbsp;５.古くなったテーブルを削除する
drop table a_table_old;&lt;/pre&gt;
&lt;div&gt;
わかりにくいのは、４.の新しいテーブルへと向き先を変えるためにビューを再定義しているところだろう。実はテーブル名を変更してもビューは古いテーブルへと向き先が自動で変わって新しいテーブルを参照してくれない。だから新しいテーブルを作ったら再度ビューを定義することで新しいテーブルへと向き先を変更する必要があるのだ。&lt;br /&gt;
&lt;br /&gt;
なお、上記のステップを使えば、そのステップの実行のいずれの時点であっても、テーブルやビューへのクエリが利用できなくなることがない。&lt;/div&gt;
&lt;h3&gt;
制御に必要な実行履歴テーブルとロックテーブル&lt;/h3&gt;
&lt;div&gt;
細かな話ではあるが、データマートを構築するまでの制御プログラムの実行を実現するためには、実行履歴を保存しておくことが必要だ。差分データ連携を実現するには前回の実行日時を、連携するテーブルごとに保存しなければならない。この実行履歴には保守の目的を含めて、実行対象テーブル、挿入データ数、更新データ数、実行開始時刻、実行終了時刻、実行結果状態などを保存しておくとよい。&lt;br /&gt;
&lt;br /&gt;
また、処理を行っているテーブルに対して、上記の制御プログラムが２重に実行されるなどした場合にはデータが破損しかねない。オンライン処理ならDBドランザクションを用いたロックで処理させるところだが、多量のデータをSelect-Insertで処理するこのようなプログラムでDBトランザクションを用いるのはあまり良い手とはいえない。処理中のテーブルを論理的にロックできるように、ロックテーブルを作ってプログラム中から必要に応じて状態を確認し、他のプロセスで処理中であればエラーとして処理を中断するなどさせると安心だ。特にオリジナルレイヤのデータの破壊だけは絶対に避けなければならない。&lt;/div&gt;
&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/5038079867765604277'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/5038079867765604277'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2016/12/redshift.html' title='Redshift設計での個人的知見のまとめ　その３'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRQxPI7_Xg0t3EqaHQ9oKsR_XD46kZ8N35iEJ49R-1kMLWYGrGKGUC0eQdNkuau6XlBdlYJwn9WwsC65dwErvTzoBM2E9XUGvp5bEvRnRerUumzXgsjCQeauHTj4vL-t5hw6yFGDX7B14/s72-c/ControlProgram.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-7279646566065282395</id><published>2016-11-15T19:49:00.001+09:00</published><updated>2016-12-18T18:36:01.597+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AWS"/><category scheme="http://www.blogger.com/atom/ns#" term="BI"/><category scheme="http://www.blogger.com/atom/ns#" term="Datamart"/><category scheme="http://www.blogger.com/atom/ns#" term="Datawarehouse"/><category scheme="http://www.blogger.com/atom/ns#" term="ELT"/><category scheme="http://www.blogger.com/atom/ns#" term="ETL"/><category scheme="http://www.blogger.com/atom/ns#" term="Redshift"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>Redshift設計での個人的知見のまとめ　その２</title><content type='html'>&lt;h3&gt;
データ連携の３つのタイプ&lt;/h3&gt;
データの源泉からデータを連携してオリジナルレイヤにマージする際のデータ連携には以下の３つのタイプがある。&lt;br /&gt;
&lt;br /&gt;
・置換データ連携&lt;br /&gt;
・差分データ連携&lt;br /&gt;
&lt;div&gt;
・スナップショットデータ連携&lt;br /&gt;
&lt;h4&gt;
置換データ連携&lt;/h4&gt;
置換データ連携は、単純にデータを全件転送してオリジナルレイヤのデータをすべて置換してしまう最も単純な連携だ。データの少ないマスタデータや、後述する差分データ連携が利用できない場合に利用する。&lt;br /&gt;
&lt;h4&gt;
差分データ連携&lt;/h4&gt;
差分データ連携は、前回の連携時点から新しく挿入や更新が行われたデータのみを転送して、オリジナルレイヤのデータにマージを行う連携だ。最も利用頻度の高いデータ連携方法で特別なことがなければこれを利用するのが良い。ただし差分データ連携の利用にはには以下のようないくつかの制限がある。&lt;br /&gt;
&lt;br /&gt;
・主キーが存在すること（存在しない場合には更新マージができない）&lt;br /&gt;
・最終更新日時が存在すること（更新されないデータの場合には登録日時が必要）&lt;br /&gt;
&lt;br /&gt;
オリジナルレイヤへのデータのマージは、ステージングレイヤを通して行われる。主キーが必要なのは、ステージングレイヤとオリジナルレイヤのデータの主キーを比較すればそれが新しく挿入されたデータなのか更新されてデータなのかは判断できるからだ。それぞれ新規データの差分を反映するSelect-Insertと、更新データ差分を反映するSelect-Updateを実行することでデータのマージを行うことができる。&lt;br /&gt;
&lt;br /&gt;
差分データ連携で最終更新日時が必要なのは、当然前回からの差分を判定するためであり、これが更新されないと連携が行われずにRedshift内に古いデータが存在することになる。Redshiftに更新が反映されるタイミングで必ず最終更新日時が変更されるのかはデータの源泉となるシステムの保守担当に十分に確認しておく必要がある。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;例えば運用保守作業で直接SQLを実行した場合や、システムが補助的にステータスを更新するなどの場合には最終更新日時が更新されないなどのケースが起こりがちだ。もしもデータ源泉側のシステムが新規開発するものであったり改修が可能であれば、ユーザオペレーションによる最終更新日時と、システムが自動的に変更を行ったことによる最終更新日時とは分けて保持することをおすすめしたい。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
スナップショットデータ連携&lt;/h4&gt;
スナップショットデータ連携は、定時で取得したスナップショットのテーブルを累積していく連携だ。例えば、在庫テーブルには現時点の最新の在庫情報しか持っていないが、日次での変遷を分析したいといった場合に役に立つ。スナップショットデータ連携では、タイムシリーズテーブルを利用すると便利が事が多い、これは日次などの定期スナップショットごとに新しいテーブルを作成する。「ベーステーブル名＋日付」のようなテーブルが多量に作成されるので、これらのシリーズ化されたテーブルをすべてUNION ALLしたビューも同時に提供する。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;タイムシリーズテーブルは、保存期間のはっきりしたテーブルに適用することがRedshift本家のページにも推奨されている。保存期間の切れたテーブルの削除はDeleteよりも高速であり、破損したブロックを作らないのでVacuumも不要だからだ。だが、差分データ連携の方でもにタイムシリーズテーブルを適用することには個人的に疑問を持っている。それは、タイムシリーズ自体がシステムを複雑にするし、Redshiftは更新可能ビューには対応していないので、挿入だけなら問題は少ないが更新する際にはビューの背後にあるテーブルにUpdateをかけなければならない。それにDeleteによる破損ブロックの問題は後述する保存期間内のデータのみに限定した&lt;/i&gt;&lt;i&gt;ディープコピー&lt;/i&gt;&lt;i&gt;をすればよい問題だからだ。&lt;/i&gt;&lt;/blockquote&gt;
対象データにもよるがスナップショットデータのテーブルはデータ量が多くなりがちなので使用には検討が必要なことがある。例えば、データの保持期間は日次で残す期間をどのくらいにするか、あるいは月次として保持する期間はどのくらいにするか、またそれらをどう組み合わせるかを設計する必要がある。また、単純な実装ではデータ源泉から全件を転送する方法が実現は簡単だが、前回からの更新分を転送することで保持するデータを削減する方法もある。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;差分のみを転送する場合、基準となるスナップショットとそこからの差分データを利用して各時点のデータを導出することになるが、この歯抜けになった各時点のデータをBIツールから参照できるようにするためにクエリによって補完しなければならないことがある。また、基準となるスナップショット自体がデータの保持期限を過ぎた場合には。&lt;/i&gt;&lt;i&gt;新しく&lt;/i&gt;&lt;i&gt;基準となるスナップショットを作成することになる。差分による転送の実現は可能だが機構の複雑さはだいぶ増してしまう。&lt;/i&gt;&lt;/blockquote&gt;
システムを運用していると、時には障害が発生する。置換データ連携や、差分データ連携ではデータ転送再開まで、しばらく古いままのデータがRedshiftにあるだけだが、スナップショットデータ連携では、それが必要とするその時にスナップショットを取れなければ情報は完全に失われてしまい、目的を達成できなくなってしまう。だからこの方式でのデータ連携では、Redshift自体はもちろんだが、データの源泉となるシステムに対しても信頼性の対策が求められることがある。また障害復旧時にはこれらのデータ連携から真っ先にデータ転送開始する必要がある。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;a href=&quot;http://torokonbu.blogspot.jp/2016/12/redshift.html&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;この続きを読む　その３へ&lt;/span&gt;&lt;/a&gt;

</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/7279646566065282395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/7279646566065282395'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2016/11/redshift_15.html' title='Redshift設計での個人的知見のまとめ　その２'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-6181374856087379836</id><published>2016-11-14T01:21:00.000+09:00</published><updated>2016-12-30T14:33:22.808+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AWS"/><category scheme="http://www.blogger.com/atom/ns#" term="BI"/><category scheme="http://www.blogger.com/atom/ns#" term="Datamart"/><category scheme="http://www.blogger.com/atom/ns#" term="Datawarehouse"/><category scheme="http://www.blogger.com/atom/ns#" term="ELT"/><category scheme="http://www.blogger.com/atom/ns#" term="ETL"/><category scheme="http://www.blogger.com/atom/ns#" term="Redshift"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>Redshift設計での個人的知見のまとめ　その１</title><content type='html'>&lt;h3&gt;
&lt;b&gt;はじめに&lt;/b&gt;&lt;/h3&gt;
AWS Redshiftを利用したシステムの設計と開発を行ってきたので、そこで得た個人的な知見をまとめたいと思う。ここで書かれることは実際に２年間稼働させ続けたRedshiftシステムの中から得たもので、これからRedshiftを利用したシステムを開発する方にも、いくつかは役に立つことがあるのではないかと思う。ただし、これはある特定の顧客向けに設計したRedshfitシステムから得た知見であるために、すべてのシステムに適用できるものではないことに注意して欲しい。どのようなシステムでもそうだが、個別システムのそれぞれの要件に応じた設計の選択がいつでもシステム設計者には求められている。また、今回のシステムの顧客の特定につながる情報や競争力を損なう可能性のある情報については記述できないため、一般論として抜き出した知見であり、詳細をぼやかした文章となっている部分もあるがご容赦いただければと思う。&lt;br /&gt;
&lt;h3&gt;
内容&lt;/h3&gt;
内容は以下のとおり：&lt;br /&gt;
&lt;br /&gt;
・システム概要&lt;br /&gt;
&lt;div&gt;
・システムの主な構成&lt;br /&gt;
・Redshift内のレイヤ構成&lt;br /&gt;
・データ連携の３つのタイプ&lt;br /&gt;
・データ収集とデータマート構築プログラム&lt;br /&gt;
・Redshiftテーブル設計の勘所&lt;br /&gt;
・パフォーマンスとキャパシティプランニング&lt;br /&gt;
・コード生成と自動化&lt;br /&gt;
・運用・監視&lt;br /&gt;
&lt;ul&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;なお、Redshift自体の機能や特徴についてはAWSのドキュメントに十分に記述されているので、こうした基本的なドキュメントの内容は既に読んでいる方を前提としている。必要に応じてそちらを参照していただければと思う。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h3&gt;
システム概要&lt;/h3&gt;
基幹システムを構成する各サブシステムから日次でデータを収集して、データウェアハウスとデータマートをRedshift上に構築し、BIツール(tableauやqlikviewのような製品)で社内ユーザがデータを任意の視点で分析できるようにするシステムだ。&lt;br /&gt;
&lt;br /&gt;
・基幹システムBIシステム&lt;br /&gt;
・日次連携&lt;br /&gt;
・データ総量 約2.5T&lt;br /&gt;
・最大トランザクションテーブル&lt;br /&gt;
　　1億はいかないくらいのレコード数、100G程度&lt;br /&gt;
&lt;ul&gt;&lt;ul&gt;
&lt;/ul&gt;
&lt;/ul&gt;
・アクティブユーザ数　1000人くらい&lt;br /&gt;
・インスタンスタイプ　dc1.large（20ノード程度）&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;
システムの主な構成&lt;/h3&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuxpc4HuN_z4YcNP8us12wQEdxlSaaFD5xrs0EdquWc_fjl3KeVqVlJezHOLGNRns2p4gLbDbXqLVHG5CBygmXvFEssSWvhloer_q9b04aMtnjpxLuiv_bhAQGiKXXH28dABJa8nMAA1E/s1600/overview.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;288&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuxpc4HuN_z4YcNP8us12wQEdxlSaaFD5xrs0EdquWc_fjl3KeVqVlJezHOLGNRns2p4gLbDbXqLVHG5CBygmXvFEssSWvhloer_q9b04aMtnjpxLuiv_bhAQGiKXXH28dABJa8nMAA1E/s640/overview.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
Redshiftを中心として、その前にデータの源泉として基幹システムを構成する各サブシステムが存在し、その後にデータマートを参照するBIツール(tableauやqlikviewのような製品)が存在する。&lt;br /&gt;
そして、各サブシステムから日次でデータを収集してRedshiftにデータを転送し、またRedshift内でデータを加工してデータマートを構成するための制御を行うプログラムを稼働させている。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;このプログラムは日次で動作すればよいのでAWS DataPipelineで日次でEC2インスタンスを起動して動作するJavaプログラムとしていた。実際のところこのプログラムはデータを収集しての転送とRedshiftへのSQLの発行が主で、それほどロジックとしては複雑なことはしないし、実行速度が求められることもない。だからJavaよりも生産性の高い言語を選択しても全く問題ない。また、詳細は後述するがこれらのロジックは決まりきった共通処理で構成されているので、データの転送元からデータマートのテーブルまでをマッピングできるDSLを定義してコード生成させることで、かなり大きな生産性の向上が期待できる部分でもある。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h3&gt;
Redshift内のレイヤ構成&lt;/h3&gt;
Redshift内は以下の４つのレイヤに分けて構成した：&lt;br /&gt;
&lt;br /&gt;
・ステージングレイヤ&lt;br /&gt;
・オリジナルレイヤ&lt;br /&gt;
・モデルレイヤ&lt;br /&gt;
・データマートレイヤ&lt;br /&gt;
&lt;ul&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;なお、このレイヤの名前はプロジェクト内で勝手に名付けたものなので一般的に使われているレイヤ名ではない。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
ステージングレイヤ&lt;/h4&gt;
ステージングレイヤは、転送してきたデータを一時的においておく場所だ。外部の各種サブシステムからS3を経由して取り込んだデータは一旦このステージングレイヤのテーブルにおいて、後続のオリジナルレイヤに対するマージを待つことになる。&lt;br /&gt;
特に各サブシステムからすべてのデータを転送していてはとても日次での連携が間に合わないため、前回取り込み時から新しく挿入や更新された差分データだけを転送することが多いだろう。この差分連携の場合には新しく挿入や更新されたデータが混在してここにまずは置かれることになる。&lt;br /&gt;
&lt;h4&gt;
オリジナルレイヤ&lt;/h4&gt;
オリジナルレイヤは、転送されてきた今までのデータを生のまますべて累積していく場所だ。生のままのデータをすべてここで維持することで、いつでもここから後続のモデルレイヤやデータマートレイヤのデータを任意の形式で（再）導出することができる。分析の要件に応じて変更が必要なデータマートにとって、導出ルールを変更して再導出することができることは大きなメリットだ。&lt;br /&gt;
&lt;br /&gt;
オリジナルレイヤには、ステージングレイヤからのデータがマージされる。多くのテーブルには主キーが定義されているだろうから、ステージングレイヤとオリジナルレイヤのデータの主キーを比較すればそれが新しく挿入されたデータなのか更新されてデータなのかは判断できる。Redshift内のテーブルの話なのだから、それぞれ新規データの差分を反映するSelect-Insertと、更新データ差分を反映するSelect-Updateを実行すれば簡単にマージが実現できてしまう。&lt;br /&gt;
&lt;br /&gt;
また長期にわたるデータ分析を可能にするために数年から10年程度にわたってデータが累積されることがある。そうするとデータ源泉となるサブシステムよりも保存期間が長いことになり、それでいつしかこのデータがシステム全体の中で「正のデータ」として扱われるようになることがある。だからオリジナルレイヤのデータの品質の確保はとても重要な問題だ。転送したデータが転送中に壊れることがないように十分なデータ転送プログラムの品質が確保される必要がある。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;Redshift内に保存されているデータの信頼性については、Redshift自体に任せてしまうことが可能だ。シングルAZではあるもののデータは複数ノードにまたがってコピーされ、別リージョンのS3へとバックアップも行える。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
モデルレイヤ&lt;/h4&gt;
モデルレイヤは、オリジナルレイヤの生データを利用しやすいように十分に整理して正規化したデータをおいておく場所だ。例えば事業部を複数抱えている場合にはたとえ同じ種類のデータであってもシステムが別になっていてそれぞれから取得する必要があることがある。そのような場合には、オリジナルレイヤではシステムごとに別々のテーブルに生データとして保持しておき、モデルレイヤで共通フォーマットを定めてそれに合わせて変換を行い、UNION ALLするといったことを実施したりする。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;この方法ではデータの源泉に対応したテーブルがRedshiftのオリジナルレイヤ内に作成されることになる。これはデータの源泉の変更がRedshift内にダイレクトに反映されるという意味で明らかなデメリットがある。だが、生データの保持していることはデータマートの構築に最大限の柔軟性をもたらしてくれる。私はデータマートの柔軟性を選択したが、何を選択するかは設計者次第だ。&lt;/i&gt;&lt;/blockquote&gt;
また、モデルレイヤでは実データとしてデータを保つ必要は必ずしもないので、パフォーマンスなどの特別な理由がなければオリジナルレイヤのデータを使ってViewで実現するのがよい。これはモデルレイヤのデータの導出時間の短縮にも、データ容量の削減にもなる。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;Redshiftのコストは主にインスタンスタイプとノード数によって決まってしまう。ではそれらを何によって決めるのかというと、１つは求めるクエリの速度で、もう１つはデータ容量だ。システムによるだろうが、私の関わったシステムではデータ容量の拡張のためにしかノードの追加を行ったことがなかったし、モデルレイヤはほとんどすべてViewとしていた。コスト削減のためには&lt;/i&gt;&lt;i&gt;常に&lt;/i&gt;&lt;i&gt;データ容量の増加を抑えるように気を配っておく必要がある。&lt;/i&gt;&lt;/blockquote&gt;
&lt;h4&gt;
データマートレイヤ&lt;/h4&gt;
データマートレイヤは、BIツール(tableauやqlikviewのような製品)から利用しやすいように加工したデータをおいておく場所だ。どのようなテーブルとするかは、主にBIツールとデータマート側との責務の分割の方針と、求めるパフォーマンスから判断する。このテーブルは、生データにより近いテーブル構造から、実際にBIツールで参照されるデータにほとんど特化したテーブル構造まで様々な集計レベルを行っておくことが考えられる。具体的には予めジョインをしておいたり、期間や組織ごとに集計しておいたりといったことだ。&lt;br /&gt;
&lt;br /&gt;
個人的には、ここでのテーブルはすべてジョイン済みのテーブルとし、パフォーマンスが許すならジョイン済みのビューとして定義することをお勧めする。&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;i&gt;システムによるだろうが、私の関わったシステムでは、パフォーマンスを向上させるためにジョインを避け、トランザクションに対するマスタはすべてデータマートではジョイン済みとした。私の経験上では５つ程度のテーブルを事前にジョインしたデータマートの場合には１０倍程度高速にクエリを実行できる。Redshiftは対象のクエリを全ノードを使って全力で短時間に実行することができるのが特徴のシステムで、逆に言えば&lt;/i&gt;&lt;i&gt;全くといっていいほど同時実行性能を持っていない。だからクエリを１０倍高速にできるというなら、１０倍のスループットを実現できるということであり、そして１０倍のアクティブユーザをさばくことができるということだ。&lt;/i&gt;&amp;nbsp;&lt;/blockquote&gt;
データマートレイヤのテーブルは様々な特化のレベルがありえるが、１つ覚えておきたいのが、生データから離れてBIツールで参照されるデータにより特化したテーブルであるほど、キャッシュに置換わる可能性が高まることだ。このキャッシュというのは単純にExcelの集計レポートとして出力されたものから、Redisをつかったものまで様々だ。こうしたキャッシュはRedshiftへの負荷を削減できるだけではなく、特定用途へ過度に特化したテーブルの増加を防ぎ、Redshiftの保守コストを抑えることにも役立つ。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://torokonbu.blogspot.jp/2016/11/redshift_15.html&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;この続きを読む　その２へ&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/6181374856087379836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/6181374856087379836'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2016/11/redshift.html' title='Redshift設計での個人的知見のまとめ　その１'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuxpc4HuN_z4YcNP8us12wQEdxlSaaFD5xrs0EdquWc_fjl3KeVqVlJezHOLGNRns2p4gLbDbXqLVHG5CBygmXvFEssSWvhloer_q9b04aMtnjpxLuiv_bhAQGiKXXH28dABJa8nMAA1E/s72-c/overview.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-7610263735573788994</id><published>2012-06-24T15:08:00.000+09:00</published><updated>2012-10-19T11:44:56.756+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mvvm"/><category scheme="http://www.blogger.com/atom/ns#" term="silverlight"/><category scheme="http://www.blogger.com/atom/ns#" term="viewmodel"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><category scheme="http://www.blogger.com/atom/ns#" term="原則"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>ViewModel設計の原則　その４</title><content type='html'>&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□導入&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;前回までは比較的ViewModel設計の位置づけ的な説明を行ってきました。今回からはとうとうViewModel設計の方法に関する原則について説明していきます。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回のブログでは、&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;４～６番目の原則として「あらゆる画面の状態をViewModelでの状態としてモデル化すること」、「イベントではなく、ViewModelの状態によって画面を制御すること」、「いつでもViewを再構成できるだけの情報をViewModelに保持させること」をそれぞれ順番に説明します。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
なお、ここでの説明がどのプロジェクトにも適用可能な正しい内容であるかは分からないことをあらかじめご承知おきください。ここでの内容をそれぞれのプロジェクトで使用するのはもちろん構いませんが、すべて自己責任でお願いします。&lt;span style=&quot;font-size: small;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□ViewModel設計ではオブジェクト指向モデリングの知識が生かせる&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ViewModel設計の特徴は、一般的なモデリングの多くがビジネスドメインを対象としているのに対して、そのモデリング対象が画面自体であるということです。&lt;br /&gt;
&lt;br /&gt;
実のところモデリング対象が画面自体であるということを除けば、他のオブジェクト指向モデリングと大きく異なる点はそれほどなく、ビジネスドメインやその他のクラス設計でのオブジェクト指向設計の開発経験をほぼそのまま生かすことができます。ある程度の設計経験者であれば、若干のコツをつかむだけでViewModel設計を行えるようになるようです。&lt;br /&gt;
&lt;br /&gt;
リッチなＵＩでは複雑な画面状態や振る舞い、そしてオブジェクト間の協調が必要とされます。このためむしろ、ビジネスドメインの設計よりも、カプセル化、継承、ポリモーフィズムといったオブジェクト指向の技術をフルに生かすことができる場でもあります。&lt;br /&gt;
&lt;br /&gt;
オブジェクト指向の技術はViewModel設計のための基礎であり、とても重要ではありますが、本ブログでの範疇を超える内容ですので、その説明については割愛します。既に多くの良書（※１）が存在しますので、詳細はそちらをご覧ください。 &lt;br /&gt;
&lt;br /&gt;
それでは、ViewModel設計の原則について見ていきましょう。 &lt;br /&gt;
&lt;br /&gt;
※１：ViewModel設計で役立つ良書としてひとつだけ紹介すると、「実践ＵＭＬ」が挙げられます。この中で紹介されているGRASPパターンは、実際のプロジェクトでうまく適用できる状況というのは少ないと思うのですが、面白いことにViewModelの設計では参考になります。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□MVVMではViewModelを通して画面をコントロールする&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ある画面、特にリッチなＵＩを思い浮かべたとき、画面の中にはあらゆる要素が含まれます。具体的には、ラベルやテキストボックスといったＵＩコントロール、表示データ、入力データ、表示ページ、タブ、ダイアログ、そしてフォント、色彩、アニメーションといったものです。そしてこれらが、ユーザの操作やその他によって変化しながら全体として画面が動作します。&lt;br /&gt;
&lt;br /&gt;
MVVMでは、こうした刻々と変化する、画面のあらゆる状態を満たすようにViewModelを設計します。そしてさらにいえば、フォントや色彩といったデザインの部分は切り捨て、プレゼンテーションロジックとも呼ばれる画面の本質のみがViewModelに含まれるようにします。&lt;br /&gt;
&lt;br /&gt;
この画面の本質であるViewModelから、フォントや色彩を含む実際の画面を構築するのはViewの役割となります。この意味で、Viewは「ViewModelから画面を生成する変換器」のようなものともいえます（具体的にはデータバインドや、データテンプレート、データトリガ―などで実現します）。&lt;br /&gt;
&lt;br /&gt;
さらにViewは、アプリケーションの実行時にViewModelの状態を常に監視しているため、ViewModelへの変更は即座に画面へと同期します。&lt;br /&gt;
&lt;br /&gt;
こうしてMVVMでは、ViewModelのプロパティや関連の変更すれば、画面をコントロールすることができます。このため些末なことに煩わされずに画面の本質に対してプログラミングできるのです。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[原則４]あらゆる画面の状態をViewModelでの状態としてモデル化すること&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
このようにMVVMでは、画面の本質であるViewModelに対してプログラミングすることによって画面をコントロールできます。しかし逆に言えば、ViewModelを通してしか画面をコントロールすることが（基本的には）できないということでもあります。ですから、MVVMではあらゆる画面の状態をViewModelの状態としてモデル化することが求められます。&lt;br /&gt;
&lt;br /&gt;
実は画面の状態は、とりわけViewModelのデータ構造（プロパティや関連）によって定義されます。つまり、「あらゆる画面の状態を満たすように、ViewModelのプロパティや関連といったデータ構造が設計されなければならない」ということになります。&lt;br /&gt;
&lt;br /&gt;
ちなみに、処理を司るViewModelのメソッド(より正確にはコマンド)内には画面の状態というものは存在しません。ViewModelのメソッド
は、ある画面の状態を別の画面の状態に変更、あるいは遷移させる役割を持ちます。&lt;br /&gt;
&lt;br /&gt;
それでは、具体的にどういったものがViewModelのプロパティや関連となるのかについて、以下にいくつかの例を列挙します。&lt;br /&gt;
&lt;br /&gt;
＜ViewModelのプロパティ＞&lt;br /&gt;
・画面表示項目の内容（ラベルの内容など）&lt;br /&gt;
・画面入力項目の内容（テキストボックスの内容など）&lt;br /&gt;
・画面項目のEnable,Disable,ReadOnly&lt;/div&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
・画面項目を強調するかどうかのフラグ&lt;br /&gt;
&lt;br /&gt;
＜ViewModelの関連（関連の先にViewModelのオブジェクトがある）＞&lt;br /&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
・子供のページやタブViewModel&lt;/div&gt;
・子供の画面部品ViewModel&lt;br /&gt;
・他のViewModelへの参照&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
ViewがＵＩコントロール、フォント、色彩、アニメーションなどを保持し、ViewModelではプレゼンテーションロジックへと抽象化された分だけ、シンプルになっていることが読み取れると思います。また、ViewModeｌは関連を通して子供のViewModelを保持したり、他のViewModelを参照したりすることで、全体としてグラフ構造を構築することもあることに注意してください。&lt;br /&gt;
&lt;br /&gt;
そのほか、 例えば郵便番号、都道府県、市区町村、番地などのプロパティを持った住所ViewModelのような、共通的な画面部品ともなるようなViewModelを関連に保持するようなケースもあります。&lt;br /&gt;
&lt;br /&gt;
ところで、あくまで上記のものは代表的な例だけであることにご注意ください。これ以外のものであっても理由があれば追加することはまったく問題ありません。何を画面の状態としてモデル化するかは、ViewModelの設計者が画面の本質にとって何が重要と考えるかに依るのです。 &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;[原則４]&lt;/span&gt;&lt;/b&gt;&lt;b&gt;あらゆる画面の状態をViewModelの状態としてモデル化すること&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[原則５]イベントではなく、ViewModelの状態によって画面を制御すること&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
MVVMでは、画面の状態がViewModelの状態としてモデル化されます。このため、画面の状態を変えるためには、ViewModelの状態を変更すればよいのでした。&lt;br /&gt;
&lt;br /&gt;
具体的には、MVVMのアプリケーションがボタン押下などのイベントを受けると、以下のような順番をとおして画面に反映されます。&lt;br /&gt;
&lt;br /&gt;
１．ボタンが押下される。&lt;br /&gt;
２．ボタン押下に対応したViewModelのメソッド（正確にはコマンド）が呼び出される&lt;br /&gt;
３．（必要に応じて、ViewModelのメソッド内で、Modelへのデータ変更や、データ取得が行われる）&lt;br /&gt;
４．ViewModelのメソッド内で、ViewModelの状態（プロパティや関連）が変更される&lt;br /&gt;
５．Viewの機能（データバインド、データテンプレート、データトリガ―など）によって、ViewModelの状態変更に対応して画面の状態が変更される&lt;br /&gt;
&lt;br /&gt;
ここで重要なのは、４．にあるように、ViewModelのメソッドでは、ViewModelの状態（プロパティや関連）を変更するまでしか行なわないことです。必ずViewModelの状態を通して画面の状態を変更するようにします。&lt;/div&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;br /&gt;
MVVMの学びたての時には、ViewModelの状態変更を通さずに、イベント発生後のViewModelのメソッドから直接画面（つまりView）を変更する方法はないのかと、そればかり試みようとしてしまうようです。しかし、これは多くの場合には誤りです。&lt;br /&gt;
&lt;br /&gt;
実はViewModelの状態変更を通さずに、ViewModelのメソッドから画面を直接操作することは、メッセンジャーと呼ばれる方法やその他の方法を使用すれば不可能ではありません。しかしそれでは、ViewModelからその画面の状態を把握する方法がなくなってしまいます。そしてその画面の状態を制御できなくなってしまうのです。&lt;br /&gt;
&lt;br /&gt;
つまり、ViewModelのメソッド内では、ViewModelのプロパティや関連を変更するだけであり、Viewを直接変更するようなことは基本的に行いません。ViewModelの状態から実際の画面の生成を行い、その状態を同期し続けるのはViewの役割となります。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;[原則５]&lt;/span&gt;&lt;/b&gt;&lt;b&gt;イベントではなく、ViewModelの状態によって画面を制御すること&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[原則６]いつでもViewを再構成できるだけの情報をViewModelに保持させること&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
原則として、「あらゆる画面の状態をViewModelでの状態としてモデル化すること」と、「イベントではなく、ViewModelの状態によって画面を制御すること」をみてきました。そして、少し前に説明したように、ViewはViewModelから画面を生成する変換器の役割を持つにすぎません。&lt;/div&gt;
&lt;div class=&quot;GHJ45FFBAC&quot;&gt;
&lt;br /&gt;
これらのことを合わせて考えてみると実は、Viewがいついかなるタイミングでなくなってしまったとしても、「View再構成できるだけの十分な情報をViewModelに保持させ、常に同期させておく」必要があることが分かります。&lt;br /&gt;
&lt;br /&gt;
これは原則というよりも、折に触れて振り返るべきテストとでもいうべきものです。ViewModelのモデル化の際に、これを自問自答することで、とある画面の状態をViewModelのプロパティや関連として定義しておくべきなのかどうかの判断基準となります。&lt;br /&gt;
&amp;nbsp; &lt;br /&gt;
なお、Viewが仮になくなってしまっても再構成する必要がない状態もあるでしょう。&lt;br /&gt;
&lt;br /&gt;
例えば、カーソルに応じて表示されるツールチップの表示・非表示の状態や、ちょっとしたデザインのためのアニメーション実行中の画面の状態（揮発性の状態）であれば、Viewとしてあらためて再構成したいなどと思わない状態ですし、それをViewModelで制御したいとも思わないでしょう。それは、それだけ些細で重要ではない画面の状態だということです。そうしたものをViewModelの状態として定義する必要はないのです。&lt;br /&gt;
&lt;br /&gt;
画面の状態として制御したいものをViewModelでの状態として定義してください。また、ViewModelの状態として定義を行わないことで、それは些細で重要ではない画面の状態であるということを表現してください。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;[原則６]&lt;/span&gt;&lt;/b&gt;&lt;b&gt;いつでもViewを再構成できるだけの情報をViewModelに保持させること&lt;/b&gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□まとめ&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
今回のブログでは、ViewModelのデータ構造（プロパティや関連）によって画面の状態をモデル化し、ViewModelのメソッドではViewModelのデータ構造に対して変更を行うことで画面を制御するということを説明しました。&lt;br /&gt;
&lt;br /&gt;
そして、「いつでもViewを再構成できるだけの情報がViewModelに保持されているかどうか」という、ViewModel設計の妥当性をチェックする簡単なテストについても説明しました。&lt;br /&gt;
&lt;br /&gt;
実はこれらの原則は、言うは易く行うは難しです。これらの原則に「完全に」沿うことは、実際にやってみるとメンドクサイですし、場合によっては実現が困難なこともあります。&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &lt;br /&gt;
私自身、ひとつのアプリケーションの中でも、重要な箇所では、可能な限り上記の原則に沿うように開発を行いますが（場合により自作のビヘイビアを作成することもあります）、それ以外の部分ではコストとメリットを天秤にかけて、原則に沿わないで開発することもしばしばです。ですから、あまりにも厳格に上記の原則を守る必要がないことについては覚えておいてください。&lt;br /&gt;
&lt;br /&gt;
なお、こうしたことが、MVVMのライブラリやフレームワークの成熟によって解決されるのか、そもそもMVVMの完全な適用など目指すべきものではなく、コストとメリットを天秤にかけて、部分部分にMVVMを適用していくのが適切なのかは、私自身まだ判断できていません。
&lt;br /&gt;
&lt;br /&gt;
私が今回紹介した原則は、MVVMが目指すべき理想にすぎないともいえます。しかし、目指すべき原則があることで、何を例外として回避し、何が解決すべき課題であるのかがハッキリと見えるようになるはずです。&lt;br /&gt;
&lt;br /&gt;
MVVMでの開発に困難がないわけでもありませんが、あまりストイックに考えずに、時にはMVVMの課題をさらりと避けて、楽しいMVVMライフをお過ごしください。&lt;br /&gt;
&lt;br /&gt;
長文お疲れ様でした。&lt;br /&gt;
&lt;br /&gt;
以上。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/7610263735573788994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/7610263735573788994'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2012/06/viewmodel.html' title='ViewModel設計の原則　その４'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-597055148556121877</id><published>2011-12-28T19:32:00.000+09:00</published><updated>2012-10-19T14:20:37.839+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Behavior"/><category scheme="http://www.blogger.com/atom/ns#" term="dialog"/><category scheme="http://www.blogger.com/atom/ns#" term="library"/><category scheme="http://www.blogger.com/atom/ns#" term="messenger"/><category scheme="http://www.blogger.com/atom/ns#" term="mvvm"/><category scheme="http://www.blogger.com/atom/ns#" term="MVVM Dialog Behavior"/><category scheme="http://www.blogger.com/atom/ns#" term="viewmodel"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><category scheme="http://www.blogger.com/atom/ns#" term="ダイアログ"/><category scheme="http://www.blogger.com/atom/ns#" term="メッセンジャー"/><title type='text'>MVVMでメッセンジャーを使わずにダイアログを表示する(MVVM Dialog Behaviorライブラリ提供)</title><content type='html'>&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□導入&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;MVVMでのダイアログ表示では、メッセンジャーを使用することが一般的となっています。しかし、メッセンジャーは理解が難しく、扱い方が煩雑であるために、MVVMを使用すべきでないという理由のひとつにさえなっていました。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;私自身、MVVM自体は使える技術だと思っているものの、メッセンジャーを使用してダイアログ表示を行うこと自体については疑問を感じており、&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt; 以前、私のブログにて、メッセンジャーを使わずにシンプルにダイアログ表示を行う方法を&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;a href=&quot;http://torokonbu.blogspot.com/2011/10/mvvm.html&quot; target=&quot;_blank&quot;&gt;「MVVMでダイアログ表示のためにメッセンジャーを使用するのはおかしいのでは？」&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;と題して&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;ご紹介もしましたが、&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;あくまで&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;この時点では&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;方向性の提案にとどまるものでした。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
しかし、この度「&lt;a href=&quot;http://mvvmdialogbehavior.codeplex.com/&quot; target=&quot;_blank&quot;&gt;MVVM Dialog Behavior（http://mvvmdialogbehavior.codeplex.com）&lt;/a&gt;」と呼ぶ、ダイアログ表示のためのライブラリを作成いたしましたので、&lt;span style=&quot;font-size: small;&quot;&gt;今回のブログではその思想と使い方について解説します。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
実は、ライブラリとはいってもたった２クラスで、全体でも200行程度しかなく、むしろコードスニペットとでもいうべきものです。このためライセンス自体を完全フリー（放棄）としますので、CodePlexでDLLをダウンロードして使用するなり、コードをコピーペーストして使うなりと自由にお使いいただければと思います。ただし、無保証ですので自己責任での使用をお願い致します。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□MVVM Dialog Behaviorの思想 &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
MVVMではViewModelに画面状態を保持し、ViewModelの状態を変化させることでViewを制御します。&lt;br /&gt;
&lt;br /&gt;
一方、メッセンジャーを使用する方法は、処理の起動であって、あくまでイベントを送るようなものであり、これ自体は画面状態とは相容れない考え方です。また、View
を起動してそこで処理を行うので、ViewModelがViewへとダイアログ表示制御の責任を放棄してしまうような面があります。つまり、メッセンジャーによ
るダイアログ表示は、ViewModelにて画面状態を保持して画面を制御するというMVVMの基本的な姿勢に反するところがあるのです。&lt;br /&gt;
&lt;br /&gt;
MVVM
 Dialog 
Behaviorでは、「ダイアログ表示という状態も画面状態の一部であり、このためViewModelの状態変化によって制御されるべきである。」と考えており、「ダイアログ表示を画面状態としてモデル化する」という思想のもとに作成されました。&lt;br /&gt;
&lt;br /&gt;
何とも分かりづらいかもしれませんが、実は表示するコンテンツをViewModelの状態として保持して制御するのは、そこかしこで当たり前のように行われていることです。例えば、ラベルやボタンのコンテンツも、Metro 
UIのタイルのコンテンツも、MVVMならViewModelで管理するはずです。それらと同様のプログラミング体験でダイアログ表示を扱えるようにすべきだといっているだけだったりします。&lt;br /&gt;
&lt;br /&gt;
単に、ラベルやボタンやタイルのコンテンツと同様にダイアログも扱うためのビヘイビアをライブラリとして提供したもの、それがMVVM 
Dialog Behaviorというライブラリです。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□MVVM Dialog Behaviorのメリット&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
MVVM Dialog Behaviorでは、ダイアログ操作にメッセンジャーではなくダイアログに対応したViewModel自体を使用します。そしてこのダイアログViewModelインスタンスを、親ViewModelのプロパティにセットすることでダイアログ表示を行い、Nullをセットすることでダイアログを閉じます。&lt;br /&gt;
&lt;br /&gt;
またダイアログViewModelのプロパティを通してダイアログへのデータの引き渡しと結果の取得を行うことができます。このようにViewModel主導で、より直感的にダイアログを制御することができます。&lt;br /&gt;
&lt;br /&gt;
そして、ダイアログの定義にViewとViewModelをしますので、メッセンジャー（およびメッセージおよびアクション）を使用するよりも見た目や機能をより簡単かつ柔軟に変更することができます。&lt;br /&gt;
&lt;br /&gt;
MVVM Dialog Behaviorを使用することによるメリットには以下のようなものがあります。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;ラベルやボタンやMetro 
UIのタイルのコンテンツ操作と同様のプログラミング体験で、ダイアログ表示を行うことができる。 &lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;ダイアログViewModelインスタンスを親ViewModelのプロパティにセットするだけでダイアログを表示を行える。また、Nullをセットするだけでダイアログを閉じることができる。&lt;/li&gt;
&lt;li&gt;ダイアログ用ViewModelのプロパティを通してデータの引き渡しと結果の取得が行える。&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;ViewとViewModelを使用して柔軟にダイアログの見た目や機能を変更することができる。&lt;/li&gt;
&lt;li&gt;ダイアログの種類を増やすのために、メッセンジャーのように専用のアクションや専用のメッセージを作成する必要がなく、ViewとViewModelの記述だけで完結させることができる。&lt;/li&gt;
&lt;li&gt;ダイアログ表示状態が明示的にViewModel構造に表現されるため、ダイアログ表示中かどうかをViewModel内で容易に判断できる。またこれを用いて、システムの都合によりダイアログを強制的にクローズすること容易にできる。&lt;/li&gt;
&lt;li&gt;ダイアログ表示のプレゼンテーションロジックがViewModelに含まれるようになり、自動単体テストコードによる検証が行いやすい。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□MVVM Dialog Behaviorの動作環境およびインストールについて &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
本ライブラリの動作環境は以下の通りです。&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;WPF4でのみ動作&lt;/li&gt;
&lt;li&gt;Blend SDK ライブラリ(無償)が必要&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;&lt;a href=&quot;http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=75E13D71-7C53-4382-9592-6C07C6A00207&quot; target=&quot;_blank&quot;&gt;Microsoft Expression Blend 4 Software Development Kit (SDK) for .NET 4&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
インストールについては以下の通りです。 &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;本ライブラリのライセンスについて&lt;br /&gt;

&lt;div style=&quot;margin-left: 40px;&quot;&gt;
Code 
Plexでは、MITライセンスを選択していますが、それ以上ゆるいライセンスが選択できなかっただけであり、ライセンス自体は完全に放棄します。MITライセンスでも本来必要な「著作権表示の保持」なども不要です。ただし、[免責]このソフトウェアを原因として発生した損失や損害については一切の責任を負いません。すべて自己責任でお願い
します。&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;本ライブラリのダウンロードについて&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Code Plexのサイトからダウンロード可能です。&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;[DLLの場合] &lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;以下のURLから「MvvmDialogBehavior.dll」をダウンロード可能です。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://mvvmdialogbehavior.codeplex.com/releases/view/80095&quot; target=&quot;_blank&quot;&gt;http://mvvmdialogbehavior.codeplex.com/releases/view/80095&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;[直接ソースコードをコピーして取り込む場合]&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;以下のURLのソースコード(MvvmDialogBehavior.cs)をコピーしてプロジェクトに取り込んでください。ソースコードは自由に改変可能です。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://mvvmdialogbehavior.codeplex.com/SourceControl/changeset/view/7708#113961&quot; target=&quot;_blank&quot;&gt;http://mvvmdialogbehavior.codeplex.com/SourceControl/changeset/view/7708#113961&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;MVVM Dialog Behaviorのサンプルコード&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
MVVM Dialog Behaviorの概念ばかり説明されてもあまりイメージが湧かないでしょうから、そろそろサンプルコードを見ながら説明していくことにします。このサンプルではViewModelの作成に&lt;a href=&quot;http://mvvmlight.codeplex.com/&quot; target=&quot;_blank&quot;&gt;MVVM Light Toolkit（http://mvvmlight.codeplex.com/）&lt;/a&gt;を使用して説明しますので、他のMVVMフレームワークを使用している場合には適宜読み替えてください。&lt;br /&gt;
&lt;br /&gt;
以下のような簡単な画面からログインダイアログが表示されるというシナリオを考えてみましょう。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; height=&quot;291&quot; src=&quot;data:image/png;base64,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&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;400&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;メインウィンドウ&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; height=&quot;314&quot; src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAW0AAAEfCAIAAAAFiQC/AAAgAElEQVR4nO29aVhUZ9qoWz/Puc7uvr4++5zdgwm9u8+XRJP+Ypvo/qpDd6diEpW2Q6Ik2ObLzmAl1R06GOwEFUcGBwQBFUHAgIpxgDgADiiWcUBAippHqIGpGIqqAopRcIjnxxrqXSMFBVTFep7r/lH1staqxVvrvXneYS0EW5LTv/x6yy/mzvv18y9kfVPYNzY+XfROkbHJcW8quKcPFz+jY67ReyROH+jhxUEyMkpwzzEy2s1LFw/Do13Do53sjCCwbIDty3Nw/rMiTp6A+NX4a8CXOpzgS7k35ro3du/Bww/F4ofTHQcOHpzeAx5a+9n0HtCf+K+lr489fJj1TeGvn3/hF3PnffDx3/+1ITFp937Bl19v+bdf/upA0RFL/8DV9s7TlpZp4ZR5Kpw0N/NzAqWJhW8ZHMexHW/EKfZixThGYqJw1GQ9arIeMVkoGC1FBIU45kKj+RsDzmGD+bDBXKDHaMrXN+Xrm/IIDukaD+kacwlytI0HtSaMbK0pW2M6oDHt1xj3a4z71DhZakOW2pCpMmSoDHvV+r0q/V6VPl2lT1fp01T6NKV+j1KfqtSnKnW7FbrdCt0uhW4nhly7Q67dIdemyLUpcm1ygza5QZNEkCjTJMo022VqjG0y9bZ69dZ69dZ69ZZ6Fcbmu6rNd1XkW+yn2+rV24i9tsvU2HHIwyY3aJIb8E/EPn2nXIudzy4FfoapSl2qUr9HqU9T6tNU+nSVIV1l2IuhNmSojZlqY5bamKUx7tMY92lM+zSm/RrTfq3pgNaUrW3M1jYe1OHk6BpzdU25+qZcfdMhfdMhrJ4N5jx9U77BnG8wFxAcNloOGy3fENg8g69G/619YFDlcKonQjMhPU5Nj7PVM5CUkekcGdX2OLU9LlZ0k6HNM3Dw84/tRq2prMRUVmIuL7FUlFgqSporSlsqSlsqSuwVJfaKks6Kku6Kku6KUz0Vp5wVp1wcuJEXbuKts+KUo+KUo6Kku6Kkq6Kko6LEXlHSWlHSWlHaXFFqrSi1VpRayksby0rblbLVb4jaBoeutnda+gcOFB35t1/+ClOJ4Bdz5x0oLKrvcU2XQWZIIid8kMgJdoNQPMIpETaD0CXio0FwiTQVUA2Sp6NIJEfrlQhpkAMa436NcT+bQTJUBoZBdHuUeJvEJLKLlAhqkAY+g2D6YBqElAhNJRSbIEJht0kD3Sa7CJsQp03axIDaJIPNJqRKaDbJIW2iQ1SC2YSqEtQmNs/gnzGPdDtVDuc02KTH2eoZ2O71yDSopM0zkC35yK5Xm858i9F05lvL2W8tZ4ubzx5vOXu87exx+7nj9nPHu84d7z53vPvc8R5enAwcBN3njnecO24/d7z97PHWs8ebzx63nj1uOXPCfOZE45kTjWdOtMrq3lv8atvgENbM63tcBwqLsKxE8OvnX7D0DwTWIDMsERtGcZNfaUgRr0SoBsElghoEkwhqEEwi2V6JsBskEzHIXqU+XUkziI5uEIWOPwfZjqQSrDkIaZBNCHw2QQ6ICYUvNyESEw6b6KmJiSGT2yaYSjCb5DBsgqokj6oSzCZW1CMYfquk1TOwJS3dOTKqwT3CaRPfPbJ/zfttWqXuZJHuZJHxZJHpZFHT6SLz6SLL6aLm00daSoraSorsJUX2kqKu0qLu0qLu0sIebhylhT2lhU4CR2mho7Sou7Soq6Soo6TIXlLUWlLUWlLUcvqI9fQRy+kj5pNHGk8eMZ08oj95pLW2+l1ROOmR05YWS//Ar59/YfGbfxFkfVN4ZZq6M4Hqy/gqkaYpSgQ1CJdECqgSYaYhuRwSOUB2ZLwGMaIGwSWCGUSJ9WK8Etmt0O5SaCebg0zGIMpNd5WTtcnUcpM9mE1UeG6CJCaETTSYTUzM3ISpktyJEhPMIy39HllHN0pDJydyHAcrik6Hrbc/ITWta3BI0eWgsfPA4ahoCQ+7DhxWdjlo2Pr69334botSpjqaqzmaqzuaqzuWazyWazqW21ScaynOtRTnthQfaik+1F58yF6c21mc21Wc212c212cu299AnnwfesTaCX71yc4inMdxbldxbkdxbkdxYfaiw+1Fh9qKT5kLT5kLT5kLj7UeOyQ8dgh/bFD+qOHtEcPWW/fWPknIeqRK+2dWd8UznvhRUHb4HBJsErExzSEe1iEKhHEI6wSYe3LcEmEnoYQEqEYRM9ukGzGUMh+r0GMtI5MOmKQNKVuj4JmEO1OhXanQrtDrpmsQVi7MKg+EhBQoXB1diZtE7bchExM0J4OrZuD5Sb72XITik30fDaxegb//N4qi7vvTqsdo4akjUItG3VtHUzMrt71u3Z3DAzWtXdg3CWIipY85o2oaEl9ewcNi7s3629vN8tqFPmZyvxMVX6mKj9TV5CpK8g0FmSaCjLNBZmWgkxbQVZLQWZbQWZ7Qaa9IAMD/cSoaEnmv+JpJR0FGR0FGR0Fme0Fma0FmS0FmdaCLGtBlrkgq7Egy1iQpS/I0uVnqfOz1PlZqvwsy/dV7/xhIeqREktL2+Dwz5+bJ+gbG/9RS4RpkG8RgzAkYuNPQygS8SENQSUy4XiqtyOjoXdkshhpCC0HwQ2i0KUqdLvl2t1yr0F2yrU75JoUuYa/F4MmIMyRVHaD1Ck31ik31ikT6nyyCXlwplC4ejpsQ7B6pk1YuzlZZDdHw97NQROTQ1SV5BMeMTh6rjZarjZaqhhca2JH6sVKQ9vl+DIpucXdd73Jet1MwRePfG+20tB3O9LeWWK+c7MmPbkuPbl+b7Jsb7I8I1mZkaTKSNJmJOkzk4yZSY2ZSebMRFtmYnNmYnPm9tbM7a2Z2zNiv6SJA32dEftla+b2lsztzZmJtsxEc2aiOTOxMTPJmJmkz0zSZiapM5MVmcnyvcmyvcn1e5Pv7k02Vla89fKLqEdOW1r6xsY5PXJYevPg+Qu+kF9142SjbWb7MlOVCM0gxY22YzgTG4RtSJVvQIQiER/GU8kZGdYchCoRhkHw5oflIJoUuSYFb5/+GoSmDyZUofhtE7bchGtCBx00yUC6OVlsgyYTjr+SHtF2dl/Umy4aTBcNpksG0yWD6TKdRoxKGkacKwiqjs7Y7UlWl/sKtfyKsZFsxsweDVleZWyioeno2vPW4qbb0updW6p3banZtaVu15b61C0NqZsaUjepUjdp9mzS7dlkSNtkSttkTttkSdtsSdtsI9j7xVqmv6KiJXu/WIttYCFoTNvcmLZZn7ZZv2ezds9mdepmeermhtTNsl1b7+7aWrtr651dW/UXzy///fOT8MiZ8xfkaoNcrWdgUGgMCo2h4urtiiu3Kq7cOll67rBMFfABEdZM5Di7RLweOTpRGlJIlcg0GmQ/72AqMwfBJIIbRK7d2UDmIFibVCcR+G6QTZMxCL9NmEMn/ucmzAmdNMbccCatm6P12SYGs8Uz+Kf3Vqk6Oss0ehytoUxrKGejguACis7LRZ3xos6oaO/457bt5h4X9hYlKlri8Xg8Hk9UtOSSzkiCll/WGWko2ztSI/7ceKPqZuKGm4kbbiduqEncUJe8oT45vj45XpEcr0r5WpPytW7H14YdXzfu+Lppx9fmHfFWhL2f/5P8COxT0j//J/lT84548474ph3xxh3xxh3x2h3xmpR4VUq8Ijm+Pjn+bnJ8XeKGmsQN1YkbbiVu0JZ/F/Hi3El45PrN6rHx8XtjEzA2Pn7t+1u5d+qDoS/DlMhxdonYjjXa2CUyURpymLo2JJ+2NoQhEdQgrDMymEHYOjK6PQpKGkIxSIMmhYBqEHUiahCZT8OoPAbZgDA1m3gHYmV8M8QpE9lkD3V6mByCzURswjObc5BNJZb+gT+9t+qiRp9ccYkkpeIyxsdrN/MPi36ydvOuC5W7L3pJvXjlvFy14ou1tRZb6sUrqRev7CG5dCUqWuJwOBwOR1S0JO3SlbRLVzGo5VfTqZQr1Am//Zms+JviJcITS4QnlwpLlgq/Wyo8t/Q/yyP+82LE/7oSsVD6l4XXly+8tXxhzfKFdcsX1i9fWL98EcmOlSvIj8A+ZcfKFegGd5cvqlm+qHr5olvLF11fvqjqL4uuRCy6FPG/KiL+89zS/yxdKjy9VHhyqfDEEuGtjF1/+O//1yQ8cvP2nUazPbXw+z1Hrqcdk6Z/ey3jVJXR2NzX625va2tva+vtdReUNxSUN0zKI7MzIIJDrDqjG6TRdrTRerSRry/DTEMOUyVCGiSfNw2hz+lq2Gdk6NMxhEEovRi5dkeDBgM3iEyTJFMnyXCDbJept8tU22UqnuVkLAapYzEI4Q4FAxahJBBCYdrElwVsiE0o3Ry2ng7nhA7L3LCWc26YHDHBPFKh0m49f4Fk2/kL289f2H7+gi/DGcllFzFSynG+kynejvnijtm6o/wSyc7ySzvLL0VFS+x2u91uZyqJLN9VcXlXxeXdCOcalBvDflJ/JP+I6KXi1146vvilk4tfKln80neLF5x/Y0HFG7+//MaLVW++eG3JizeWvFi95MWaJS/W4cyvWzI/5Z1I8vhkREVLUt6JxDaoWzK/dsn86iXzby2Zf2PJ/GtL5l95c/7lN+ZfeGNB2RsvnVn80unFL59c/PK3i18+/trLN9NShD/7PybjkVvVTZauf2RWfH6g/Iu8c2sLz6wrLtVoLH1uJ+qRwsp6Hz0ym30Z5tJVFok0Wo82UtIQVomgHRlmGpKPjKfySIQxI+PrYOouUiJIDpIs0yRj+pCpk2TqxHr19nrSIKpt9aqt9aqt9SruHETpu0HW13qZjE2UE+Qmk+zp8E/o0Ls5asqIyX7u8VdL/8Af31tVrtJuPXdh23kvpEf6eCMqWpJSdokEU8aZBsXb/4y9Y7buKL+MsrP8clS0pJk3oqIluysqMVIJzjUoN4b9VHa04Nhri46/tujb1xeeen1h6RsLv3vj5fNvvnzhzZcuL1lwbekC6dIFN5ctqF62oGbZgrplv8dIWfE2+qG01ykr3sY2q122oHrZgtvLFtxYuuDa0gWVSxZcfnPBhTdfKnvz5TNvvFzyxsJTbyw88frC468tupWeIvzZ/zkJj1RJbzRZurYWX43JO/O/s4+uzslfnX9Qo7H0uV1tbW1tba29ve7i29WnGr73xSMzN7nri0RIlaAGOdpoPYIlI5x9GQvFIAbKInfWNITsy7AuLWNdFcI3HcPaiyFzEKwLU6/eXq/aXs80iJJvImYig6D6YDI1m0wtN6HZhHUINo0xN5yp5hx/JW2Ce+TdVRUq3fbzlzASyy5jJJVdjoqWdPBGVLRkR3nlToRdFZVnGpTvfLG2xtK8q6Jyd8UVlKhoiZk3oqIley5cJUm7cDXtwtXzctWG3/7fsm+Ljke8cnz5KyeXv3Jq+SvfvfXKuchXyiNfuRD5SmXkK9ciX7ke+cqNd16pfucPNSv+ULdCiIF+YlS0JOXdlbSSuhV/qFvxh9oVr1SveOXWO+E33gm/Fhl+NTL8cmT4xcjwssjwc5HhZ94KP/3X8FN/Df/2r+HV+3cL/5//NmmPRKUXRqRmv5W1752cvVEFqVg+0tbWiuUjZ3WV5aaKCT0yq30ZlsXvOGwSsR4xWSdMQ4hp3SaKRHQsq8u8oyFaynhqFut4KrUjw2IQoiPDTEMSyTSkXrWtXrWtXrX1LmoQ5ea7OH4aJB5hFmyCjptMYkKHt5uTxbbMBBsxMfcP/PHdVZfUxuTyq0nlV5PLryaXX02pqML4JG4r//jImnVbd1+4tvvCtVSE8wrNyi/iai3NqRevpV68tueilOTzran8B4zZtif90nUa5Qrt+mf+R0NJ8cl3l5x6b0nJe0u+e2/JufeWnI9+80L0m5dXv1H5/uJr7y/+/v3Ftz9YXP3B4poPFtcRpKyOJg+esnpV3Qevp6xeRSup++D12g9er/7g9eoPXr/x/uvX33+96v3XK1e/fulvb16IXlIWveRc9NIz0UtLo5eejl56Jzfrlf/xb5P2yPL0A5H7M1ccSn/3m91/K07G8hGyX2PoNhi6DTwemZ3JXf40BF0wQjeIyXrEZC0yWX2RCP9oCM0gPGkI11AIbTCVfRzEaxAVxSB3VVvuqrbcpRnEC+swKk8XJp6XyXV2/LMJc9yEcwiWd6UJuWgNvTfH3D8g+uDDy0rdnss3d138fjdC6qUbGHsI0i7fJEnHqLyZXnlzb+XNvZU3MwgqlIao2K/qrW0ZSGFm5S2ULJIrOPu83KZxUWXcNO8pxfnTpZ+s/O6TlWc/WXn+k5UXxCsvfLqy8rOVVyUrrklWfC9ZcevzFdWfv1P3eWRdTGR9TGQDO28zkcW8XRfzdl3M29Wfr6j+fMWNf6y8Lll5TbKyUrLy8mdRFz6LuvBp1Pk1UefWRJ2TrKo9lv+nX/y/E3vkFNUjbx/MiMrfE120c/XxpP99eotGY+lFxkfGx+6Nj93j8khgB0SYEsGgG8RkLTJZC00Ug3zDkYYwb5OhdWQwgxygjKfS77IjF6cyJ3T5h0LYcxDSIHeVm+toBlEk3FVMaBCeBMR3m0ycnvi25MTH3ITvfj/k1mFyQT3t3pwDxGp6c//AX/7xz8oGVe6N+oyqGozMqposgn3Xamnsl9YeQLlee+B6bfb1OoyD1+sua5tWrdsgb+nMuV5H5S5KLsn3OIe81KNc0Zk3v/j/aS6VlX+5puLLNRfi1lyIW1O57pOr6z65tu6T6199fGv9x9XrP65d/2Hdxg9lCR82JHzYsOlDJScf0VBs+qhh00cNCR/VbfyobuNHNes/qV7/ya31n1z/+pNr/1pT+a81lf9ac3Gd+OI6ceWmL++Unnrr2f85gUdOMTzy7uHdq46l/NeJ7R+Vblpzbr1GY3G7CI+43cPDQ57+flaPzPLkLteACE0iRxkSKTRZMInw37CLpiHs61NZ0hCejszEQyGTNkidchOG1yCKhDrFxjrFtBtkumzCteRkApv4NGjCN5tD9nHqe1xfHsiN272n0mgrrFXn3pYfvCk/eFN+8JY855Y8F+O2PPe2/BBCXrUCJZ+goFpZUK2Umpo/+Hqzyu4oqFYevkPhGy5qcAoJimpUJDcaW7Yvmqu7drlyc1zllrirW9Ze27r2+ta1N7avvb19bXXi2rqktfXJsQ07YhU7Y1W7vlDt+kK7K0bHxz9pqHb9U7UrVrEztmHnWlny2rrktXXJX1YnfXk78csb2768tu3La0lf3Ujd2nCi6Ghu7pb3o/k8copK1fWbWr0t7XxpevnpjAsnsi4d3195TCbTkh5xu1yDA57e3l6mR4KjL2OjGYSY5WWRSCH2QAqOIVWeh4awGmQfX0eGY1XItBikTrmpDtcHYRCvR6bdINMxdOKHTRi5Cd9KE2o3h0xMTllaqs1W0er3Y5J3XJWrGjp66tq7Se4i1NsdTGQMGuyOBrvjsy1JXQPDDcRbeQdKz4QoGKT+8cU2k0F38azh4lnjxTOmS2dMl840XTpjvnTGUnnGUnmm5WppS1VpW1Vpe1Vpe1WpfWK+o9Fe9V1b1XctVd+1VH3XfPU7y5WzlstnzZfPmjCqLlRXnD28L/Ov/zG3wdYstXezeKR3bOwUwyNXrt148OD+4EDfoKdv0NM3NDg4NOgZGui/Nzoydm90eHhoYGCgv7+vv78P9Ugw9GVY0xC+TITgsJGc2WVZXUZLQ5g32tEWhjBmZPgMgkjED4PUMQ2i2Iis/piCPr6ulZP4I5SZyE1Y7x7msQnrA02KjeZ6e+dXh4veio0Lfzc6/N3oP+Ks+uN7OH+i8meM6L+hvIrwZvRqOqsoLJkE7y9Z9f7Hz815+99x3nlmzjvPPrXy2adXPhcWNTfs3Xm/fveF37z3wm+jf/fb6P/491Uv/vvqF59ZPf/Z1fOfXT3/uffnP7ea5Pf4i1Xzn/sbUr5q/nOr5j8XPf/Z91589t3/eCbqd/++8oXfrnjht+/M+83bc//nW8/9+q/PPv3XZ58Wv/Rc+kfRmi5HZXMbbSwV8Yi5meIRc8ux0vPXvr/F5PrN6hu3a67fuE2WHD195lCNLHgmd3kkQgyIWGgSwR5sM9k0hGtal+0+XfZVIZyDqfXIhC4hEZpBNtMMUqtIqFVsxKDoQ44xKYmg+mAyJZWwpCfM3IR13AR7WpIvS9e4V5pw3je8T2MqsbTKne6m/gEzFQsTz6CVDRvJAIVmPoZQWiZJKw+DM8h1e/cpM31OhuaR5lPIbbvHtKZDNbLcO/UTcqhGdkxrCobJXV/6MkUmSxE9DSEkYjDnGya4U4Z7fapPQyFcBklmXRLCYRB6DoIahIA0yPpaEp9yELo1ahAmY5PJ5SY+9HS4FpuwPicJsQn9eWvMe3O4VtPTFr/S7vEjHz5Ae1ZjoRH/K0WO4qND++jKA+Yya/xKbrIVMy541qbB+ceY2tz42+Zk77Al4fDIVI8YzH0ZLomwZCLExcHsyzDHU1lvtGPeZcd+ix33UAhqEJ96MQyDbMChGUQejzOZBKSGg0mmJ/G+DZ1MatxkQpswuznoShOOp8CaWG/zw23CeI7JtKiEaZNixCZ+qaRpEiqZsk24PNJMFUTLyR+zRGiTu0Um72IzUiIUg2Do+e61Yzy4jCqRyaUhfDnI1ikbpFa+vka+voYpEXaVTMIgftjEx4HYCW3CvxCWPmjCvaCeNv6aRR1/Zb1jmOc5Jjw2CQWVsHqkGZUIv0cCPrl7bJJ9GbQ78w1TItiAiL4pV8+45R95AjPzRrspdGQmGkzlMwi9C8M0CEE8XSJyPoNwKOOrGvlXPgtlOqd1JmMT6hAsz6AJezeH9TY/9ImNM52YcKmE2Qq42ksA+zicHjlJZZYlMpk0ZIoSIed30TSEkEhTrq6J9uwyxmOHEIMgEkE7MlzryngMMmEvZhIGQWF4xBeDfMVB8NiEOW7CZRN03Rpz0Rqmkr2MR8AyExPaiAnXk1/pNvFjuAS/8oN7uKSX6RH8cJYWGuhszklLMx1+iXDwLRvso6pN1IebNdmONVENQq55J6BIxGQpNFm+MSHzu0ZzgdFcYCQ90nTIgD/LM1fflKPH/gQRAyJa436tcb/GuE9jzNIYstSGTLUhQ43/HxnG/39AHpsqZ3/UEOUef+rNdYxV7azrQbxzMeggCKMLQ1CH83VdA4H86zr2uZivfIZ9NqeOQnydgof1VDbcVWy4q9xwV7kRAX82Sr1yU71yU71qU71qM8EWmWqLTLVVpt6KPdmkASexQZMk1yTJNclyTbJcm6LQpii0OxTanQrtLqVul1K3G/umVPo92HenNqSrcZVkaIyZ5DPWtEZ8uERnytY1ZpP/LkffSF4qhwxNeQZCJUZzPnFd4Sox4VBU0mgtQi7UI7Q+DnZtE7COueIqYWs+PjY9uj4YLZq5EISH3rHxn89FPEI9NLtHpsUg/kjk2EQSYRqkkMMg+QZznqEJlUiOvhGTSLauMVtrOqA1kQbBJJKJP9QPz0TSVPo0lZ7FIKhE8McdMp8SgkuEdnPddBqE8AhikIav6xq+rsWYokGCxyaYSjCbbJOptzWw2kSbIveqZKdC57WJSo/ahFCJ1yb7tMZ9WhPTJth1QlWJ1yYFbDYpZNjEd5VwNQp/VDKhTabiEdoRNe6+xj7PZDH5gbGv3xcMGL2c6HH6SHS9fTq3Fy0DjQtH7epVu3pVFNwqp1tJoHC6FU633OmS9+A0IMgcLpnDVY/jxLhLUNeN0VPX3VNLUEPSheK4w0Y1SSeF2z7S5bjd5fPG00LXJKhmoae6q+cOE7TeqNR2O2u7nbUOZx0C+RXUe78dV32PS9bjklG/QXmPS+50Y2DfNf7Vu9wq+oXRi10t5MWjYbu0tOSF14tDXJb4hcp5JffR4WkU/rS7KTRzjbvPJ49o3H17i4+/Kv4MAACARkbxtzSVsHuksc/zqvizx489AAAANF4Vf9bY52H1yDg6O9PY53lVLAn46QIAEIS8KpZM4JET4BEAAHjh88gJc8sJ8AgAABPhk0dOgEcAAODGV4+YwCMAAHDgi0eaT5ibwSMAAHCBeQRdnkrzSDN4BAAAflCPnGR4pPlH6hFpjiQ2pzzgpwEAIQLNIyfRdWg+eEQdLnhKam6e6sc3xwqfyqtSc20gzZEIkAiPyUJ/hL5l7jhZj+TFCGm7uM3lQqHEPdFJAgAQWI9MAE0WeTHCMKHE7duOk/WIrSqLdnCmWQAAYAX3iHlaPYLkEUIbUWirysKKwoSRUcJFUnPz48fNsZQXN8KpezGTDrJtk6YgD0vbkVQA68mQheExn4bj5eTJ0H41tLw5VvgUmhyh9qG95kmXAOAJw+sRDP89wtq03ObyMKIZu83lYfQmirVPfANUFrTWSB6QNeMgt0ctwzwZWiGrthAReD2CZijEa3U4ctoCojZggAYIKegeMXPcp+e7R/JihMhoAt4Iqe2K+aeekgugOqB5hBiz4Mo4BDSPsJ4MWsgQnFcKxDbkuXmV8RiREbGlOkooKcE/l5baAMATDr5+hPY8NNQjk8xHaE0IH6SktucJPELmAr7kI9IcCbM3IWVvz9jJ3EALUXeQZ8soXCQ1N7vN5WECSqBnYqvKis0pxzRnJmQX8G8XAGYHNB9heMTSfILAz3yEOmY5dY8wxkc4d+TORxTo/AtVGbinSijZE3s+QoK5IykmkvxFcnI2QacGCCn41o+gszgc99f4Oj6CDkPYqrIEU/IIOl+DmoJotM2xwqd8GR9BUxhpjkRAsYM6XCAQUH4p9vERhOZY4VPUAwpgnhgIKV4VSxr7Bk5ZWkn6WDzCeZ+eOpya6pPJAnOKJC9GiEyR+OoR5sExSFMg3Y2ncnI20TzCcTLeaZeomE/DqFkGY3aZcm7kb4GuZ0H9QktwACAUYHrEm4+Q/9b31DTf78veQQgIbhjLAAC/wTxy2tJK0kfxiHn6PYJ2KwJOXowQFnoAgJ9weqRvbBz97+F+ewTtAQU4GUFnXrmgmHAAABtKSURBVILHaADw4+VVsaSp31NibSXxeqTE0lpC2KWxb+BHdJ8eAACzyatiSVP/QKm1tdTaWmptK7W20T2C0QQeAQCAA6pHWqkeQbKUpn7wCAAA7GAe+c7WSuL1CJafYIBHAADg4lWxxNw/8J2t7QyB1yNnrG0k5v4BEXgEAAA2RGKJpX/grK2NpB/zSP/YOFoKHgEAgAuRWGLxDJxrbiPxeuScrR3jrK3dAh4BAIADwiPt5wm8HilDSi0e8AgAAOyIxBKrZ6CspZ2kf9zrETtBuxU8AgAAByKxxOoZLG+2l7fgeDCPeMbHiaL28hbwCAAAnIjEEptnsKLFXtFir2i1V7R6PXL/YmvHhVY7hm1gEDwCAAArIrGkeWDwYmsHiWf8PukROwl4BAAALjCPXGrrIPF65HJbx2WitBk8AgAAByKxpGVgqLK9k2QA88jA+P3Ktk6SloEh8AgAAKyQHrlC4PXI1fZOklbwCAAAHIjEktbBoav2rqv2rip7V5W9y+uRa/YujCp7V+sgeAQAAHZEYknb4PC1jm4MaUf34P37P587TzB4//71ju7rHd3Sjm5pR3fb4DB4BAAAVkRiSfvgMGYMDK9HbnR0f0/QDh4BAIADzCM3Oh0kXo/c7HSQ2IfAIwAAsCMSS+xDw7guunpudvUM3X/w87nzBEP3H9zu6sG41QUeAQCAE5FYYh8aIY1xG/VIdXdPdXfP7e6e2909HcMj4BEAAFgRiSUdwyOYMTC8HqnpdhL0dIJHAADgQCSWdA6PIMZw4h4Zvv+gzuGqc7jqHM46h7MLPAIAAAcisaRreIQwhqvO4RrGPfLgQX2Pu77HhdE9Ah4BAIAdkVjSPTJa3+OWEQw/ePDzufMEIw8eyJ1uEsfIKHgEAABWRGKJY2RU7uyVO3sVzl6Fs3eE9IjK1YuhdPX2gEcAAOBAJJb0jN5TuftIRh48/PnceYLRBw/Url41oRIneAQAAA5EYolzdFTt7sWkoXb1jmL5yOiDh1p3H4bO3ecavQceAQCAFZFY4hq9h7lC19un6+0bxfKRew8e6nv7Db39ht5+fW8/eAQAAC5EYol79B5pDENv/z3cIw8fmvo8JO57Y+ARAABYEYklvffGcF30e0x9nnsPCY809XswGvs8veARAAA4EIklfffGCGMMNPUP4B4Ze/jQ4hkg6RsDjwAAwI5ILOkbG8NcYfUMWD2DY6RHbJ5Bkn7wCAAAHIjEkv6xMdvAIAnukfGHj1oGhloGBjE84BEAADgQiSWesfGWgSGS8YePcI+0Dg6RDIyNg0cAAGBFJJYMjI1jrmgbHGobRDzSPjhMAh4BAIALzCO4LoaG24eGxx9hHnn0yD40TDI4Dh4BAIAdkVgyOD5O6GLEPjSCe+T+o0cdwyMdQziD4/fBIwAAsCISSwbH75PG6BweuU96pHN4hGQIPAIAAAcisWRo/D5qjPtkvwbr52AMQL8GAAAORGLJwPg45go7Oj6C/T898r/swf/lBACAC+z/cl6xd10lwP+fnuveWKHJivGNyapz94NHAABgRSSW6Hr7C022wkZbYaOtqNHmvjeGe+Sw0XLYaCkwWgqMFq27DzwCAAArIrFE6+47bLQcNloPm6yHTVYX6ZECQiL5BjN4BAAALjCPFBgtuEqMDI/kG8zgEQAAeCA9QqqE4pF8oznfaM4zgkcAAOAE80i+0VJgshSYLIdNFrpH8ozmPINZAx4BAIAD0iP5Rku+yVKAegRPRgzmQ+ARAAC4EYklGndfntGMeSQf9QiWieQZzIcMTeARAAC4wDxyyGjOw1ISI+ER570xUiK54BEAALjBPWIwkyqheASTSK6hSe0CjwAAwI5ILFFjHiFU4iQ9gkkkR9+UoweP+IS9w3qp8tq5ssvADHHxcpXFagz4Fw3QwDySazDnEirxeoSUyEF9k9rVCx6ZEIu1bXz8/mOIGYvx8fuNTa0B/6IBGphHcgxNuEoQj9wjJNJ4UN+oAo/4QLvdGeiG9uRHu90Z8C8aoCESS9SuvhxDE6kSwiOj9zCJZOsbs3XgEZ9o73AFupU9+dHe4Qr4Fw3QEIklKlffQX3TQT2uEq9HDuqbMIkcAI/4BnhkFgI8EoRgHsnWN2UTKkE9gktkP3jEN8AjsxDgkSBEJJaoXL0H9I2IR+7hHiElsl9nUoJHfKC9Ezwy49HeCR4JOjCP7Nc1kirBPdIzeu8A7hHTPu3MeUQdLhDakBJbVVaYUOKe6gFtVVkCgSA2p5wskeZI0LczSnunm3rN28IFYVKzm71B8IatKi9MGEvs6Y4VhuVV2abc9vJiwmNzpDyfFR6T5/v2PobbLBV6f4Vpi/ZOd8CbDUBDJJYoMY/oGrHejXMU8QgmkSytSeFy/1g8EiZcFCZ4SmpuxkrAI7581vR6xG2WhgkEAoEgDDwSGmAe2adrJFXSg3jEtF+Le0TpnH2PNMcKnxLglyNuFmmORIAHvpetKis8JgsrD4/JIt+Gx2Q9JnbBPILsSzlgbE55XoxQIBAIBE9Jzc3kZqR93ObyMGLHvCo1z6/T3jWxR2xVeQJvhBNucMcKw4hzi3U/toV7twmTms2sHkEO5f0Usg0LBOExMeHYXnmUFw3EZ+F78XrEeybkNnkx4cipPqYdljzPmcpHusAjQQfmkSydiVSJ1yOkRLI0RsWseyQvRki25LyYT23UVIV8TevIYB55/FgdTqQkrPkIeXDMGpgdsNdYua0qi1CV91DU1yz44hE0pDmxWONE//7nxcTYfMhH3GZpGKEh5DX6ibZwolWjHiH1QX46t0fQz3UnxSS5kb1o5y9g/KbgkdBBJJYoXL1ZWhOpEq9H9mlNWVpjltaYGSCPkDkFRl6MEEkH8CZNiMO7O/aWPA7qESLv8KYb6E+pXSr8xGjHR+3GxN7VS73m2T0izYkVUP/I58WE01ryhB6R5sSiXQ/MFFQjePfKYyQm6Efwe4TawXHHCoXU3CfcRj2sd9OZ8Yi9qzfgzQagIRJLFC53JpZ2aE37UI9gmUimxpgxux4h2q23XxObU46+JYLPI9j2eVVq0hSomNDODr9H0N4Qrb/DxN49sUekObGkIJAG7O3XYO12Qo/Qmi7W8qlymcAjZDtn9Qixmbdfk1dle0zpcHm7ZrPqkW7wSNAhEksUTneG1oirRGdCPEJIZK/aIJ8pjzTHChehPQV0aIMATz2o+QgOt0ewcQ1hDm4KirB89wjt+Pz44BHK33NmAyZ3mVo+gnY6fPcIo8HzdKMo50/7dFoheCR0EIklcqd7r8aYoTVhKvF6JFOLSyR9Bj3ikeZIqK0XG61ojhVGEs0edw3rVA6PRx4THRkynSE0pA73uV8z4ZgIii/5CDIU4o4VhoXH5D1+7I4VRiEDrkKp2Y0Of/g+PuI2S8OIT8QGXH3xCK3/YqvKIwaAbVFeEdjCBeE26vgI+kuBR0IZzCPpGiOpEtwjjtF7GVrjXo0hXWNIUxvkPa6ZW4eGDluQGQfaoUCbOpJRs+QLjPTBqwxkXzJJ8cUj+EGYZ8jE7uijXvP0XkBsjhSZTwnLyUnC2iQ6YoIMuGJ74/M1lL4EMT5KFHhtRR4qTBgV5Vs+wjhV0l/eqRkBMhGDni2WNNE8gvyO3rOdNo84+gLebAAamEfSNEZSJQ6vRzTGdMIjDTPpkScGhkcCG+x9kB97gEeCEJFY0uB0p6kN6RpjusaYoTF6PbJXY0zXGNPU+j0qPXjEF4LKI0j35IkK8EgQgnlkj9qAqWQv6pF0jTFNbdij1qeq9TLwiA/YHf2BbmVupAc0xaW0QR52R3/Av2iAhkgskfW4UtUGUiVej6SpDXvUhlS1PlUFHvEJe0/APfLkh70HPBJ04B5RGUiVeD2yR21IVRtSVYbd4BHfAI/MQoBHghDMI7tVelIluEe6R++REtml0teDR3wAPDILAR4JQkRiSX2Pa5dKT6qkG/UIJpGdSvCIT3Q6+x8+ehTohvYkx8NHj8AjQQjmkZ1KPa4S1COkRHYodeARXxga8XQ4++09wAziGQKPBB2YR3YodaRKCI+MjJIS2aHU1TvAIwAAsCMSS+odLswVmEq6R0bpHklR6O6CRwAA4EAkltx1uFIUbB7BJaLUJYNHAADgBvNIskKXQqQkXo/sUOpTlPpkhR48AgAAD6RHkhX6FKV+B+mRrpFRQiL6JIWuDjwCAAAHIrGkzuFKUugwY6Qo9V1ejxASSZSDRwAA4ATzSKJch6kkRYF4BJcIeAQAAF5IjyQqcJV4PUJKZLtcVwseAQCAA5FYUutwbZfrSJVQPSLXbZfrtsm14BEAALjAPLJNrsVUQvEIKRHwCAAAPJAeIVXi9ch2uW67XLtNrt3aAB4BAIATzCNbGzCPaLdTPYJLZGuDtsbhBI8AAMCKSCypcTgxV2AqwT3SOTK6Ta7d1qDd2qDdAh4BAIAbzCNbMI80aLfJtZ1ejxAS2dygqekGjwAAwI5ILKnpdm5u0JAq8XqElMhmGXgEAABOcI/INKRKvB7Z0qDd0qABjwAAwA/VI5otVI/gEtkEHgEAgBvMI5tkGlIlXo+QEtkk09wBjwAAwIFILLnT7cRcgakE8YgM90gCeMQ37B3WS5XXzpVdBmaIi5erLFZjwL9ogAbmkQTSIzLSI8OjZDKSIFODR3zBYm0bH78f6GchP8kxPn6/sak14F80QIPwiJpMSTqHCY+QEkmoB4/4RLvdGeiG9uRHu90Z8C8aoIF7pF5NqgT3SMfwKNajAY/4TnuHK9Ct7MmP9g5XwL9ogAbqEax300F6hJTIRvCIb4BHZiHAI0EI5pGN9WpSJYRHRkYTGtQJMvVGmXqjTF3d7RR9Ch6ZAPDILAR4JAgRfSqp7nZirkiQqRMa1B0jpEcIiWwIUY80xwoXSc3Nvu/S3gkemfFo7wSPBB2YRzYQxkiQzbZH1OECb4THZAW8RhCm4BF3oFvZkx/tne5AXxgAHW6PDI+SEplhjzxFtNXmWOFTsTnlAa8UAvBIMAZ4JAghPUKqxDvOSkpktjzisVVlhQklZnN5mDdH8f40L0ZIFAptbCV5MUJSQ7TXeVVq7Pi0w7rN5UKhpKEqSyAQhAklbuo2wsl6pAs8MuPR3gUeCTpQj2ygeWRDgDziRjYgS6Q5ErLXI83ZJDU3M0tsVVlEiTqc8AKZVrjN5WGEgMjXbnN5GNKfcpvLw7znQzk3XwCPzEKAR4IQmkc2cHukZ3b6NYzEQUB6hKYYZsnjx+pwgdD22GOryorNOULqQ0gcAe00YUkK+VPymMg2k+7X2Lt6A93Knvywd/UGvNkANESfSqq7ewLrEW9gbdhWlSVAEgeynZO9GDJ9YJTgLT8vJhJLWGJzykk1kL0bct/YnHKaR6jbTN4j3eCRGQ97N3gk6PDJI+vrVbOSj+CgjZnWzh8/9qBpC7NEmiOJzdkbJZS4iX2TYiKx4/uSj6CjKuCR4AzwSBCCeWR9vYrTI+vrVbPsEXTgIy9GiHVe8ggdPCYUwCx5TIx3EC5ojhU+JWCMiTymjo+gHqGlQmGTHB8Bj8xCgEeCENIjpEooHsF+MMseQTs7STl7hfRpFLwXwyxhHhBV0mPu+RrasAsxLhMZNdl8xNEX6Fb25Ifd0RfwZgPQQD2CqcTrEbJ0Jj3yRAEemYUAjwQhNI+sr1eBR6YOeGQWAjwShPB4ZAQtvQ0e8QG7oz/QrezJD7ujP+BfNEBD9KnkNt0jI3SPxINHfMPeAx6Z8bD3gEeCDswj8eCRaQE8MgsBHglCJvZIPHjEZ8AjsxDgkSCE9Eg8q0fiwSOTodPZ//DRo0A3tCc5Hj56BB4JQlCPxINH/GRoxNPh7Lf3ADOIZwg8EnTweGR0fb2apLrbKfr07wE/XQAAghDRp3+v7naixugYHv3F3OfBIwAA+Ap4BAAAfwGPAADgL+ARAAD8BTwCAIC/gEcAAPAX8AgAAP4CHgEAwF/AIwAA+At4BAAAfwGPAADgL+ARAAD8BTwCAIC/8HpEpiYBjwAAwAXuEcQY4BEAACYHeAQAAH8BjwAA4C/gEQAA/CVUPKJW12ZkpMXHx8dBMCI+Pj4jI62mRgq1N4WYsPZCgZDwiFpdu2fPnra2th9++CHQDzwPxvjhhx+cTmdeXt7331+G2pts8NdeiBASHsnISGtraxsfHx+C4IixsTGPx5OYuA1qbwrBU3shQkh4JD4+/ocffhiE4I0ffvghLi4Oam9qwVV7IUJIeCQuLg5awoTB1RKg9nwJ8Ah4BGJwEDziX4BHQsUjAxC8we+RQJ9dsAd4BDwCMTAAHvEvwCPgEYiBAfCIfwEeCRWPeCB4g98jgT67YA/wSEh45NGjR4G+0oI9Hj16xOURqL0Jg6v2QgTwCAQe4BF/AjwCHoHweMAj/gV4JFQ80g/BG/weCfTZBXuAR8AjEP394BH/AjwSEh55+PBhHwRvPHz4kMsjUHsTBlfthQjgEQg8wCP+BHgEPALR1wce8S/AI+ARiL4+8Ih/AR4JFY/0QvAGv0cCfXbBHuAR8AhEby94xL8Aj4SERx48eBDoKy3Y48GDB1weeSJqryMhcmHRdd0MHZ2r9kKEEPKIe+YjIfLphELplHcvTIiMTCicxvOZVPB7ZEY/ujAhUkDET+dGau0z8SH2hMiFhVLtTBzazV17IQJ4ZDrDT48ENgLkEXtC5NynIxPI99LChITC6zPzQeCRmQI8Mp3B6hGttPCnxB/bwutWvNR6/Wni7++av72G7VWYEIm+SIh8mr7XTEZAPCItTEAlQob1eqHAG09jFWC9Xvh0ZIK0MAErRXI3K1FVAuJo3hJiM/DIDBIqHrl//75r5iMh8umNhVJKkUX69E/nSjXtlNftmsi5Py2UWlwuF/Ya26twYyT5QiAQYBtICxPmRm5sn/mTv3//PpdHZq72MGPybyMt3Ph0ZILL5bJICwUCQeTGQpfL1a6Rzv3p01KLy+Vq3xg5Fyt0uVyFCWuklvaNkXOJw5Kv2zdGLiyUamboF+GqvRABPDKdwfSItDAhMqGQfIuZQiMtXIiogdwL9Qh5HI208KdPR1pm/uQD4RG0wdNDWrjRm5AQHsFeuFwul8sS+fRPC6Wado104cJIDSJaWgmhIfDIDBIqHhkdHbXNfKyLmLMuuwwtyV4XgZZkr4uIWJddnr0uYl02cy9yY8peyvI5cyKUM3/yo6OjXB6ZudrLXhexLrucWV6Wve4nz0TU6G02m01Znj0nYh36wmaz2Wz6dRELsstqqIX49gJqzIlYR24/Q78IV+2FCCHkEevMx7qIOXHZZWhJWfa6iHXZ5NvsuIi47LKy7HXPRMTpGHthP0VfWK1Wq6JszpwIxcyfPL9HZuhDy7Lj5kSsYxTr4iJ+n112B3ujKMvGtiFfoNvo7pT9/vcRd3TIzowS5jGnPcAj4JFpC6ZHrIqyOT95pgy7qMnXirI5AkF2mQIvFAhC1iNWqyJijgBVSVl23Lrssux1ERFxmH91cRHP8HgE24DY2Jq97oMyhS4u4pl1tC8CPDKThIpHRkZGLDMfccvmoOn0M8vidBZL9fkDPyFKDpyXY1uePxBH5NzL/mvZnLgD5y0Wy4G4ZbQXFovFIj8/Z84y+cyf/MjICJdHZrj2dHHLnvH2QZbFWSwWXfX5Z/Ba+0li4kasUH7+APaC2Ov3B85XWywWi0VOVjyxAeWYy+IOULef/uCqvRAhVDwyPDxsDtaIWzZn/7mGQJ+FeXh4mMsjwVx7QRJctRcigEcCHLfP7f/JT545d1sb6BMBj/gV4JGQ8MjQ0FBTMMWXSA9o/zlZoE+nqampaWhoiMsjwVZ7QRhctRcigEcg8ACP+BPgER6PaEh+7B4ZHBxshOCNwcFBLo9A7U0YXLUXIhAe8RrjifWICYI3+D0S6LML9gCPcHpkg0xDUt3tfO3H7JGBgYFAX2nBHgMDA1wegdqbMLhqL0R47dO/V3c7UWM8sR4xQvAGv0cCfXbBHuCRkPCIx+MJ9JUW7OHxeLg8ArU3YXDVXogQKh7p7+/XQ/BGf38/l0eg9iYMrtoLEcAjEHiAR/wJ8EhIeKSvr08HwRt9fX1cHoHamzC4ai9EAI9A4AEe8SfAIyHhkd7eXi0Eb/T29nJ5BGpvwuCqvRAhVDzidrs1ELzhdru5PAK1N2Fw1V6IAB6BwAM84k+ARzg9svEJ8ojL5VJD8IbL5eLyCNTehMFVeyECt0dGRjc2aDYQVDucr332I/aI0+kM9JUW7OF0Ork8ArU3YXDVXojw2md/r3Y4NyDG6BgZ/cU8wiMbnxSP9PT0qCB4o6enh8sjUHsTBlfthQgTe2Tjj98j//rXvxwOhxKCN7haAtSeLwEeefI9kp6eKpPJzGZzoC+24A2TydTY2Lh58yaovSkET+2FCD55ZOOP3CO3b19NS0vT6XTdEBxhMpkKCgouXjwLtTeF4Km9EMFXj9z5MXvk8WPP999f3rx5UxwER2zevOnixbOPHvVB7U0h+GsvFAgVjwAAMHOARwAA8BfwCAAA/gIeAQDAX8AjAAD4C3gEAAB/AY8AAOAv4BEAAPwFPAIAgL+ARwAA8BfwCAAA/gIeAQDAX8AjAAD4C3gEAAB/AY8AAOAv4BEAAPwFPAIAgL+ARwAA8BfwCAAA/gIeAQDAX8AjAAD4C3gEAAB/AY8AAOAv4BEAAPwFPAIAgL/we0RLcsfheu2zfwT8dAEACEJe++wf1Q7XhgYtCXgEAIDJwesRuZYEPAIAABe4R+RaEvAIAACTAzwCAIC/gEcAAPAX8AgAAP4CHgEAwF/AIwAA+At4BAAAfwGPAADgL+ARAAD8BTwCAIC/8HkkQa4lAY8AAMDFa5/9447DhWYe7B6p6nRs+qbotc/+AQAAQCPhm6KrnY6JPXK4yVbV6bjjcAFTwjk7VE8HbEcOeAUGnFn6BgOHX/VztdNR0GRj8cj+wqL9BjOqkplgY6igQR8KNaNsmA7oh5VraL/RTF8YAYTzS5ytbzAAML5fP9lnMO8vLHrhd/MFv3nhd3U9LvDItHnkR6USXy61gDf4Wb8sA93afzweqe1x/eaF3/1Z9Ibgl/Oezy46cqbVDiqZNmb34phmjzSwXGoBb/CzfVkGvMH/GGxyptWeXXTkl/Oe/8tbKwSrP1jzs1/9KrvoSF2PK9toAZWElEd8VEnAWzuoZLo94pdK9hvNtT2u7KIjP/vVr95YuvzjT2ME69ZvXf3Bml/Oe/43L/xuX2FRx8goAAAAD/sKi37zwu9+Oe/5N5Yu/3DNPz5fGy/YlrJ33fqtq/7r47+8tfIX854HAACYkD+++vqy5e98JI75fG38VxsT/398AYvTXskryAAAAABJRU5ErkJggg==&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;400&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ログインダイアログ（メインウィンドウのOpenDialogボタン押下時に起動する）&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
次にViewModelを見てみます。まずはViewModelのクラス図を見てみましょう。&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; height=&quot;260&quot; src=&quot;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBQgMFBofBgYHCg0dHhwHBwgMFB0jHAcJCw8aLCgfCQsTHCI3NCcMEhwgKTQ5LhkgJyw0PTwzJC4wMTgyNDL/2wBDAQkJDBgyMjIyCQsNITIyMjIMDRwyMjIyMhghMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAFjAiIDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDuvB3gTw5LY2rXPhXQJZGghmnmkt4yZpHt1JMjleSxrb/4V34X/wChP8Of+AsX/wATR4H/AOQfaf8AXvB/6TJW9Xy9Sbu/fkbJGD/wrvwv/wBCf4c/8BYv/iaP+Fd+F/8AoT/Dn/gLF/8AE1vV5NqHiPUrQXNpHqdy1+8yQ+G7mR2LLp+qN1SQkn/QVDjPbiujCUZS/h1XpZv0vZv5dfIUmuvz8ju/+Fd+F/8AoT/Dn/gLF/8AE0f8K78L/wDQn+HP/AWL/wCJrj7rxEmk3F59ksElmC2Vgl25ld7uW4VlDXq7mz5IGcKqs3Qkkg1Zb4j6rb86tYxpaLPHp91qcttNCJ7G8jPzww3DZH2aYYbJYEdMV0PB1P8Al1OT0Uo9L3Sem/dJdX23tPMuq8/wOn/4V34X/wChP8Of+AsX/wATR/wrvwv/ANCf4c/8BYv/AImuX0X4najdyxRSWFpBcSSG5aJlb5PD7W29Xxu4ZwQvpnPFcVpvi7xGti8b6hqEs8yyavperMzFrGxsy/mBpyc5j8sAc/xDBrWnl1X7dWK267p31+Vm/QOZdF5f1/XVHrv/AArvwv8A9Cf4c/8AAWL/AOJo/wCFd+F/+hP8Of8AgLF/8TXOaX4r1iZI49KmsVkS0i1m+vL5Xc3s88fCqFkTGdpJYs3Xp3qrN47v9VicaVDZWUf2T+2dQa4RnLtcqw2WwV48bArfMc54+WsfqdTrUVlpN3+FPb7+lvnYcZLt5r8P80db/wAK78L/APQn+HP/AAFi/wDiaP8AhXfhf/oT/Dn/AICxf/E1wumeO9RgtoY9Fs/Mkjs4NQKG1mlOozTRcJGbcjZkL95sjJ4HBrf8KXTTahdtJbyQM0VnO9vIObdnhkO2QEDlDx0FFXCTV3Oo9Fzx89Uvxvfr2Eprt2f5f5m3/wAK78L/APQn+HP/AAFi/wDiaP8AhXfhf/oT/Dn/AICxf/E1vUV5vO/55GlvIwf+Fd+F/wDoT/Dn/gLF/wDE1ieMfAnhyKxumtvCugRSLBNNBNHbxgwyJbsQY3C8FTXc1g+NxmwuQoy7QyWdtGOtxc3ERVViXu08jBQBySQBzV05u696W4mi1/wrjwr/ANCb4a/8BYv/AIij/hXHhX/oTfDX/gLF/wDEV0FFfUGJz/8Awrjwr/0Jvhr/AMBYv/iKP+FceFf+hN8Nf+AsX/xFdBRQBz//AArjwr/0Jvhr/wABYv8A4ij/AIVx4V/6E3w1/wCAsX/xFdBRQBz/APwrjwr/ANCb4a/8BYv/AIij/hXHhX/oTfDX/gLF/wDEV0FFAHP/APCuPCv/AEJvhr/wFi/+Io/4Vx4V/wChN8Nf+AsX/wARXQUUAc//AMK48K/9Cb4a/wDAWL/4ij/hXHhX/oTfDX/gLF/8RXQUUAc//wAK48K/9Cb4a/8AAWL/AOIo/wCFceFf+hN8Nf8AgLF/8RXQUUAc/wD8K48K/wDQm+Gv/AWL/wCIo/4Vx4V/6E3w1/4Cxf8AxFdBRQBz/wDwrjwr/wBCb4a/8BYv/iKP+FceFf8AoTfDX/gLF/8AEV0FFAHP/wDCuPCv/Qm+Gv8AwFi/+Io/4Vx4V/6E3w1/4Cxf/EV0FFAHP/8ACuPCv/Qm+Gv/AAFi/wDiKP8AhXHhX/oTfDX/AICxf/EV0FFAHP8A/CuPCv8A0Jvhr/wFi/8AiKP+FceFf+hN8Nf+AsX/AMRXQUUAc/8A8K48K/8AQm+Gv/AWL/4ij/hXHhX/AKE3w1/4Cxf/ABFdBRQBz/8Awrjwr/0Jvhr/AMBYv/iKP+FceFf+hN8Nf+AsX/xFdBRQBz//AArjwr/0Jvhr/wABYv8A4ij/AIVx4V/6E3w1/wCAsX/xFdBRQBz/APwrjwr/ANCb4a/8BYv/AIij/hXHhX/oTfDX/gLF/wDEV0FFAHP/APCuPCv/AEJvhr/wFi/+Io/4Vx4V/wChN8Nf+AsX/wARXQUUAc//AMK48K/9Cb4a/wDAWL/4ij/hXHhX/oTfDX/gLF/8RXQUUAc//wAK48K/9Cb4a/8AAWL/AOIo/wCFceFf+hN8Nf8AgLF/8RXQUUAc/wD8K48K/wDQm+Gv/AWL/wCIo/4Vx4V/6E3w1/4Cxf8AxFdBRQBz/wDwrjwr/wBCb4a/8BYv/iKP+FceFf8AoTfDX/gLF/8AEV0FFAHP/wDCuPCv/Qm+Gv8AwFi/+Io/4Vx4V/6E3w1/4Cxf/EV0FFAHP/8ACuPCv/Qm+Gv/AAFi/wDiKP8AhXHhX/oTfDX/AICxf/EV0FFAHP8A/CuPCv8A0Jvhr/wFi/8AiKP+FceFf+hN8Nf+AsX/AMRXQUUAc/8A8K48K/8AQm+Gv/AWL/4ij/hXHhX/AKE3w1/4Cxf/ABFdBRQBz/8Awrjwr/0Jvhr/AMBYv/iKP+FceFf+hN8Nf+AsX/xFdBRQB8OeNLaKK/ultYYoo1nmhghjACwxJcMAI0HQIOBWHXQeO/8AkI3n/XzP/wClL1z9AH2r4H/5B9p/17wf+kyVvVxvgzwtevY2pj8ZeIIVMEMiQRpa7YFa3XiMvbMcIOBlifUmtv8A4RG//wCh58Sf98Wn/wAiV4ksFPpKn36/5Gil6mvWZP4Y0yW4Saewja7QG3tbrJzFG4ORtBweCeoOO1R/8Ijf/wDQ8+JP++LT/wCRKP8AhEb/AP6HnxJ/3xaf/IlOODqL4KsV9l6vVPpsDkuqFuvCGkzmU3enRymYJb6luLf6QlsDtwueDFngjBzznNQyeBtIkjkS7t7u5ikCx3aXNxK5dIWJASWWQkbGJPBFS/8ACI3/AP0PPiT/AL4tP/kSj/hEb/8A6HnxJ/3xaf8AyJWiw9b7OI7NavS1rfdZW9EF12J08Oacswljso1uRH/ZiXAJ+W0DZ2hM44PtntnFNtfDOnQ25htrTZaFWtmttzHMVxncPMJz8+4/xfSov+ERv/8AoefEn/fFp/8AIlH/AAiN/wD9Dz4k/wC+LT/5EqXhavWsujWr6bfdd2BSXREV14F0aZUElnMgSP8AsqFoZpEY2OB8k0sTqWXjoxI/M0XvgbRp8eZYyRYT+y0+yyyR5sf7jiB1yo9DkDtUv/CI3/8A0PPiT/vi0/8AkSj/AIRG/wD+h58Sf98Wn/yJVqhW6Yl/3dXoK6/l8iF/AujkR+Va3MBjQaZaTW08qMtmmMJJPE6khcD7zGrmn+HNPtXZrC1McjLHZzPuY+ZDZqQoIYn/AFak+575qH/hEb//AKHnxJ/3xaf/ACJR/wAIjf8A/Q8+JP8Avi0/+RKmWHrfaxF+jV3r1/PULrpHyNeisj/hEb//AKHnxJ/3xaf/ACJR/wAIjf8A/Q8+JP8Avi0/+RKy+oz/AJqf4/5D5vU16xZE+23SoBut4Majf+lxqcg/do3UH7KmZSCAyt5TKaf/AMIjf/8AQ8+JP++LT/5ErT0bQ47JCqXVzcuzNd3d7cbd97NK3Wdo1Ufu0wowoAUBRwBW+FwjTvVce69ROXY0KKKK9UgKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPiDx3/yEbz/r5n/9KXrn66Dx3/yEbz/r5n/9KXrn6APt/wACf8g6z/69oP8A0mSugrn/AAJ/yDrP/r2g/wDSZK6CgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD4g8d/8hG8/6+Z//Sl65+ug8d/8hG8/6+Z//Sl65+gD7f8AAn/IOs/+vaD/ANJkroK5/wACf8g6z/69oP8A0mSugoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA+IPHf/IRvP+vmf/0peufroPHf/IRvP+vmf/0peufoA+3/AAJ/yDrP/r2g/wDSZK6Cuf8AAn/IOs/+vaD/ANJkroKACiiigArz/wAK+Gbe5s7d73UvEkkzwxXdzL/aF0PNmmgUk7VmA+ZiegA9K9ArlfA//IPtP+veD/0mSvNzGbSXJKS7lwXcX/hCbD/n+8Sf+DG7/wDj9H/CE2H/AD/eJP8AwY3f/wAfrdoryfbT/wCftT72XbyRhf8ACE2H/P8AeJP/AAY3f/x+j/hCbD/n+8Sf+DG7/wDj9btFHtp/8/an3sLeSML/AIQmw/5/vEn/AIMbv/4/R/whNh/z/eJP/Bjd/wDx+ud+LOmm6gt41neEvdRWyTr1gaRJBlf901z1nPq8upWcvia3ltHAn0pbZ+jCxtfnlUY6XMrNjj7oFepQw83G6xklpKfLrf3VdW11vZ+hEn2j5r+vkeh/8ITYf8/3iT/wY3f/AMfqCz8MaPcKGsNY124iOQk8OqXTK5Q4O11nP3SCOtcto/jDU2ubQR6jrF1Z3HmxNPfRW6rei3iJD6dHb4cAsOjjofWqfhrWtTlWyjs9SWwjlN4Lw2kEI4s522+TGYyoI7/JzznnmqeFq/axX9693ZK0730vf3H/AFscy6LyXnc7z/hCbD/n+8Sf+DG7/wDj9QXPhjR4Nv2zWNdgDMLWAy6pdDzp5Oix7p+S/YdTXDNrl0sNre6nN5rwTS6JrNwqAG50uadk3Sog6wsFOAo56CiHzhbxXE8MJmur6C9Hnxqxt9OeXagj8wHBSEZBGCM8GqWEn9vFT39h1+O70+5XvbqlYTkv5f73yV/1Vj0D/hCbD/n+8Sf+DG7/APj9H/CE2H/P94k/8GN3/wDH65DTvE/iW7k3Wi3iQi4fSNQib7MItPtFcqPL3N5nmI21ueCTgLjFZUHxU1qJFk1AlobfOk+KlEYBudTkaUKYyF48tkjzjA+fnpxKwdb7GJi+kkpPRu9l6tq3q0PmX8vl/X4/ceif8ITYf8/3iT/wY3f/AMfo/wCEJsP+f7xJ/wCDG7/+P15peprgvLaVmuLu/is01e+tY1AOopJcsHjSJB1WNzjC9QO9M0zU9V0037zOEvHa0ku5pBkaQ+pg5JU9rIPgZOOBnjitfqU7e5jruyaV3q3JR77ap3trtYnnX8v975Wuenf8ITYf8/3iT/wY3f8A8fqGPwvpDuyxavrryrhrm3XU7rdbrJnHmxifjeAcZHPauW1vxfrGntcRQ6o13tFu8Gqzom7SjqM20+esSop8sDcuUH+0SOKzr/Vb7Sri/a3vrq7lxZWrX0wi3W8dyzgttVY1/dA8ZAXON3GaypYSo/8AmK6J0tXrdx+5Wl6+RTkv5fN+SO6ufC+kQgfa9X12EEi2hMmp3Q82aQ8KhM/Vz0HU9qm/4Qmw/wCf7xJ/4Mbv/wCP1wutXWtERrrsd0EF7ZzaXJdGDzZLeVzn7SlmxX5ZFODtGR64r1euXExnFL/aZO972d10/wAxpr+Xz/Fr9DC/4Qmw/wCf7xJ/4Mbv/wCP0f8ACE2H/P8AeJP/AAY3f/x+t2iuP20/+ftT72VbyRhf8ITYf8/3iT/wY3f/AMfo/wCEJsP+f7xJ/wCDG7/+P1u0Ue2n/wA/an3sLeSOWm0aOzubQ2N/rZ3zPaXMc95cSLPD/Zty2HhmlYcOinpkY4rta5jW/wDj4sv+vh//AE03ddPXt4CTa9+TfQzl5BRRRXaSFFFFABRRXP8AjSSRbY+TcXEJMkFu0kLsrCOa9iBCSoQRvQkcEVUI3aXd8oHQUVw2us+lhxp97fzRNb3F+9rczyuYJ7KNSGhumfeA+7aQJAOhXBzmXUPFuqQzD7Dbpc2Qkh0y9fycfZZbp41Ia9e5BJQuD8ts4/hJzkjVUf5JLXWP5fmJvujtKK4fSNR1AkxaNLYwyGS81Ca5ukZwsUepOAscCSIcuzdd2Bjoc8Ry+ONXnhlfS49KtvJh/tK9inVpPtdyBJ8tu6SR4VfLPJDZz0GOX7F9HHt+geS/wneUVlatq8lvavJGiGRYzdRqehlCcbh6Z96qaRJewztFqGqz348tNRiuJljDROzspXEKqMEgEZXPXJPbNQ8129RX7ep0FFYN3e6hHewqLy3+yOkrG1WP5vNgCcyXBY8c8AIvuTW9Uyj5rX3kUYV1440uGR0catLIhEN0LazuJBDI8asA00MTDJjdT97vzUf/AAn+mf8APr4j/wDBdd//ABiodE/4+L3/AK+E/wDTTaVt15FbGNNpQj2LUfMyv+E/0z/n18R/+C67/wDjFH/Cf6Z/z6+I/wDwXXf/AMYrVorL6+/+fcR8vmZX/Cf6Z/z6+I//AAXXf/xij/hP9M/59fEf/guu/wD4xWrRR9ff/PuIcvmZX/Cf6Z/z6+I//Bdd/wDxij/hP9M/59fEf/guu/8A4xWrRR9ff/PuIcvmZX/Cf6Z/z6+I/wDwXXf/AMYo/wCE/wBM/wCfXxH/AOC67/8AjFatFH19/wDPuIcvmZX/AAn+mf8APr4j/wDBdd//ABij/hP9M/59fEf/AILrv/4xWrRR9ff/AD7iHL5mV/wn+mf8+viP/wAF13/8Yo/4T/TP+fXxH/4Lrv8A+MVq0UfX3/z7iHL5mV/wn+mf8+viP/wXXf8A8Yo/4T/TP+fXxH/4Lrv/AOMVq0UfX3/z7iHL5mV/wn+mf8+viP8A8F13/wDGKP8AhP8ATP8An18R/wDguu//AIxWrRR9ff8Az7iHL5mV/wAJ/pn/AD6+I/8AwXXf/wAYrZsNQt7mNHsZfMhdVvLWXBHmwTKCDtYA/MpHUA+tR1l+BP8AkHWf/XtB/wCkyV24Ou5X5opWsTJHQUUUV2khRRRQB8QeO/8AkI3n/XzP/wClL1z9dB47/wCQjef9fM//AKUvXP0Afb/gT/kHWf8A17Qf+kyV0Fc/4E/5B1n/ANe0H/pMldBQAUUUUAFcr4H/AOQfaf8AXvB/6TJXVV514O8G2EljatJda8GMEMrrHf3ShWe3X/VxJMAAD2AAHYV5mZ2suZsuB3NFYP8Awg2nf8/fiP8A8GN3/wDH6rXvhjRLZd2o6xrNrEMK08+p3SqpY8Zdpx1PvXkKK+y59krf8E0Onorm7Xwlo86hrLVNcniPzwzxandFZV/2HWf+tTf8INp3/P34j/8ABjd//H6Gl1cuz0/4IvSxsXFjbzbftdrBNtYXMHmKD5E6dGjyOCmeD1olsbd2VprWB5Ez9lnZQWtvMGD5Lkcbx1xjPesf/hBtO/5+/Ef/AIMbv/4/Wbcad4Xik8u68T30NxkRizk1e4Dl5MYAhNxn58jHHPargn/y7dV+i2XXqD87G7a+FNFgfdZaBpME2fMW4ihQMrsCMiRVB5BPep4ND0+Ir9m0uxiKbjaMkaj7KZz83kkDjzj1xjPesgeD9JLFV1LXjIAJngGpXW5I3JwWTz+jEHtTbjwnpERQT3/iNWdvstsv9oXn7yYRs2Mib+4rHnAqnJvepWf+X39rhbyXdm0NGsPLKDTbL7Ocma08tdkpkbJ3xYx87cnjmnSaXZuqrLY2rxoVltYmQEWzw/dMKEcGLtjGO1ZX/CDad/z9+I//AAY3f/x+kPgfTh9688RAdSTqN3wB6/v+1Rzf9PKn83z+8LeS7F6Tw1pLy+ZLommPc5Ei3rRIXV0AwRMRnKADHNSvomnsrrJpli0bt9pvYjGuLubI+a4THJyBycniue07RvDd5n+yfEWpXm3BmFtqty3k7848zZcHG7Bq/wD8INp3/P34j/8ABjd//H6uba+OdZW0S7duoLysbH2G337/ALLB5237KLnaNwg3Z2+ZjON3OM4ph0qzJctYWhMgEV+xQZvY1XAFycchV45zxxWV/wAINp3/AD9+I/8AwY3f/wAfo/4QbTv+fvxH/wCDG7/+P1nftUn2/rUPki9b+HNKijZLXRtNit2ybm0jiUJcFgAfNiAwdwAHIptp4X0eBWFjoWlW6OPKvIooUAu4wDxOqgZABPXNU/8AhBtO/wCfvxH/AODG7/8Aj9H/AAg2nf8AP34j/wDBjd//AB+r9o/+f1XvLz/ELeS8i3b+FNGhGLTQNJhXct1sjhQDz4futgL1iJ4PUdq065y58H6VCrNcX3iNY1Bnmf8AtC7+SNBknAm7AelPTwTprAFLzxGQfmU/2jd8gj08+lN3+OdR9L/0wS7JHQUVg/8ACDad/wA/fiP/AMGN3/8AH6P+EG07/n78R/8Agxu//j9Z2X80vu/4IzeorB/4QbTv+fvxH/4Mbv8A+P0f8INp3/P34j/8GN3/APH6LL+aX3f8EB+t/wDHxZf9fD/+mm7rp6818TaFcWL2z+For28vvNkSxtb+9naOaX+yrv7yzSMM5HH3c9Nygkj5++InibxhczFPHDX1uwJmg0tl2xRKruA1rEOGCksofLkj+I172X/Do2ZSPsuivmP4Q+LPiEzKmhWsuqaeCLecXxIisIkMYIt788gwxYwo34BJEZr3z7d4q/6F3w1/4Hy//IVdpJ0FFc/9u8Vf9C74a/8AA+X/AOQqPt3ir/oXfDX/AIHy/wDyFQB0FVNT0i2vIyl/G7xEq7KjspDxOCCksZBBRwDwRWV9u8Vf9C74a/8AA+X/AOQqPt3ir/oXfDX/AIHy/wDyFTT7PzQFmHwhpiJIpt55RIps7yW4lkd5rZ1I2/apXZgoBOAGABJI5Jps/gvSpJN81rOXLJePGJpAjXNsV2s1qHC7k2LztzxzUH27xV/0Lvhr/wAD5f8A5Co+3eKv+hd8Nf8AgfL/APIVV7SXSchW8kWbjwhpcoAa3njIZ7lJoJpEdXu5Cz4uInU4mc5I3benHAxieJPAv2lQmlaZYwxGP+ypblbmWP7Pa8jB0+FNriBWJUO4GfQE1pfbvFX/AELvhr/wPl/+QqPt3ir/AKF3w1/4Hy//ACFVQqyW0n3Q2bFxp0EsZSePdEVNrIhP3omXGMj1FYj+EZ1jcQa5qU1w4Szk1CdlD21jE/ItmgRMEKWwcbiTy3Aw/wC3eKv+hd8Nf+B8v/yFR9u8Vf8AQu+Gv/A+X/5CqYzfR+bFb/gCXmlW013GR4ou4bmNTdQaRG0PNm7AMzxPEzbZmUAndwfukGuhrk9SstavFC6v4L8G3sYPnJDc3bsqSgEZVHsTyASOlcX/AMJB4iT/AJEU22pN1jtI7+a6gmn7/atTuLUbWSPnb/aEIHBCkth05Prbsgsdzon/AB8Xv/Xwn/pptK265TwRPqUhuTr9nbW14Zo2v7aBtywTnSLTIRz/ACywHQMwG49XXzGL+J+pvEKoza7ZRzJFNcFLh1a5tISrfv44euyXGMp6Zz7Verj/AIgadeeXFNotlJc3lvIL62t4wd13bycOiAA/62M+hPHFLCwTdqkrX92L7Ppfyva/kD8vVGrP4xslcrZ2+oXsiyppV8lrEW/s+W5Gd1xIcDbEv3iCdvcVt15d/wAIvfx2VsZbG4e8kvIfEetoikmCSefJMygcC3TAPYYqnZeG71rkHWVuI7oXLTSXEenys2q2E7EBZdaWTb5RgbGCBtxjGRz6MsHT/wCXeIta8Xpdya66dL9eiauRzeXmvx/yPXazJ/E+mx3CQT3qpeOpu7W1Kt++hXd92TGONp43Z9q8kj0HxRaxF7ewvZJ7MnQNDtgrH+1bKdpAXjjxyAjxYxkfKeeuNKb4b3onhXTozDNBZxtpupsp8uPWILvJEsgB/wBflsjk4OcVawFNfxcZF7qLXR93vprF/NrdA5PpH0+9L/P8z0rT/EmnXckqafeLNLCRb6jGoP8Ao0rZ43kYOMHoTjvWlXjq+C9VtVvksra+lz9klvJkBB12NUzP9lfjJmJbIByc7e+Km1PRLt1uj4X0bULXTWFus2nRwvEdQeOX979jsWVD89vhThRv6AnFKeBhf9zilbRJvzUd9V/M7K3R67hzvrF90v66npVp4ht5riWKKOcSxCOa5dgNrrdhiPKIP8IU5yB7Zo1rxDb2IjN3HO4kkTSoRGB8s102AXyRwD16n0FeXXmhXDG+PhTQru1tmFoUtxaPGL+2gZ/MWC0cRFsr1AILDjPNS/8ACPXCgf2bDeSxm8tNSazh0+WCPTljYhjbW0xJxtUFiAFHr6V9Sp9aztopRtre0W7vpq3o+1hOT6LzX3HeT/ETQomYT31wmxxpt3cGCXy7W6YgbZ7zy9o5YdXAroh7V4zqmj3zLfIsfiTzHuTe6bpaWrGHV9rRbTc3fk8K7Lz/AKQgwM/VfG9hqFx9o8vwrHDeARTWd1BZzSS31zGiZay1aIhUEG0ADknB7mn/AGfB29nWav8AE3rb4dbaaXbu1dK2+9nzeXkvvPZaK8q1vSZzds9tpLapK0kMBs7+xlP2GCHGW0nXAQqjktycZz34MWnaBfi6DaobmG8W6NybqKwlZtSs5XIAk1pZNvleQwGCBtxjGRzzrAxtd4lbc9rPe3be3TmtYXP/AHf61/yPWqKKK8o1CsvwJ/yDrP8A69oP/SZK1Ky/An/IOs/+vaD/ANJkr1sq6/L9SJnQUUUV65mFFFFAHxB47/5CN5/18z/+lL1z9dB47/5CN5/18z/+lL1z9AH2/wCBP+QdZ/8AXtB/6TJXQVz/AIE/5B1n/wBe0H/pMldBQAUUUUAFcr4H/wCQfaf9e8H/AKTJXVVyvgf/AJB9p/17wf8ApMleXmmy9S4GlNLqAkUW9lYvb/8ALxcPMweJuf8AVWwiIPb/AJar/jj61dW1vdQyavJFDbKksMV3NwlleyNHgyyngGWMMATjuAecHpKK8ynUS+x0cJW63VvMto86vb+0Db49UNjpst0oe/im8tL5P7Ok3GO4BHyzyqBkEZIypzg1VutZuAqA34OmFplsr+7vpYRdeW6bN+rRqzEbTJty4DgZJbANei3emxTNGZWkBjf7bAFx80hhdf3mR02ufTmrVdqxcdP3Tf2Wr7bpdN9tfwJcfP8ArU81T7XOspuPEN1KY7SO+tZrG5bZJdB7nD+am3cdqAHKhW/iU4GNGz8Q6RHNdrrl9p4Z2iP9nyEFr9JdPhGILTkt5h4ACkk8Cu5oqJYpO/NSf8sbO1tn28vxGo9n/Wn9fM84a8vLaIrqU9xFi3tInM0zqNKa4u5hvup4WU/uVC7iHXdjDMByGaVeTnYItVj1IJdmPT7lS5WZW0V2xHJNLISDIx5MrD0wMAelUVX1xa3orXV66J2S7evXqHL/API/hY8yXWZljB0HWdRurwwyT+IYC7OdOuljBy9m5IjeOXcAoVQeRtOOEmns5HkXRtVvNUsVS1u7+Pz5Jw8Qvn8zaWds5iUbgM5HGO1emSIWBCSPGSCqyLjMZI6oGBHy+4I9RWfpmhC3Zmm1C9vZm2xSXVzsysMOcKiwogwhZj93OTyelXHFx19xrrFX31T106bLsS4+fk/L/gmFrPifSrhM6Nq9pOQUi1O+smDNpmlyzLvLXMZO0Mo65BABYfdyMo3JlZE0rW9SlsGuUtre8jnZjcRtYzF0h1AsSyhwOd5KnIUggY9EorCniElaFKX927vb8N109WU15+X9f10OAktpbdWaLVdYdo7qLTLNZbiQhLO4uYgVmQt82Vkbl97DseBSR2s8gLtrOtLIbyTSwFnbbHYm8ddiQZx93odu9f4WGBj0Cin9bf8AJ1u35dtvn8wce3y8t/yv+B5zf3ghWWKbVpbaEXIs7bU7y5mA02M2CPi4ukmRj5jlgA0yjJ68AE0M3F40CXmtajJFtvVkkt5ZE+2pZ38aoWk3lvkToTIW/vMQTn0aiq+uaaU3fe9+tmr7Xvrffp31Bx7ep5ZeatHLbsPEGt3cL/ZVk0iMTMh1S5eOTcfLRh5jFlUEFWA/u/Nz6ZZf6tP91f8A0EVPRWOJrp/DTa+1a+i9FZW/zBL/AC/IKKKK5CgooooAxNb/AOPiy/6+H/8ATTd1t6lomn3ihdX0yxvYwfOSG5jVlSUAjKo4PIBI6Via3/x8WX/Xw/8A6abuunr38u+H5mUyO3toolC2sMUUagQwQxgBYYkGAI0HQIOBxUlFFdxIUUUUAFFFFABRRRQAUUUUAFFFFAGXqXhXS7xgdX0+K9AHlJbXJLRIQT8y2LkruGSN2zfjjOOK1KKKAOY0T/j4vf8Ar4T/ANNNpW3WJon/AB8Xv/Xwn/pptK26+axfxP1NohRRRXOMKKKKACiiigAooooAKKKKACiiigAooooAKKKKACsvwJ/yDrP/AK9oP/SZK1Ky/An/ACDrP/r2g/8ASZK9bKuvy/UiZ0FFFFeuZhRRRQB8QeO/+Qjef9fM/wD6UvXP10Hjv/kI3n/XzP8A+lL1z9AH2/4E/wCQdZ/9e0H/AKTJXQVz/gT/AJB1n/17Qf8ApMldBQAUUUUAFcr4H/5B9p/17wf+kyV1Vc//AMK48K/9Cb4a/wDAWL/4iuXFUOb7dupUWalFZf8Awrjwr/0Jvhr/AMBYv/iKP+FceFf+hN8Nf+AsX/xFcX9n/wDT78P+CVzeRqUVl/8ACuPCv/Qm+Gv/AAFi/wDiKP8AhXHhX/oTfDX/AICxf/EUf2f/ANPvw/4Ic3kalFZf/CuPCv8A0Jvhr/wFi/8AiKP+FceFf+hN8Nf+AsX/AMRR/Z//AE+/D/ghzeRqUVl/8K48K/8AQm+Gv/AWL/4ij/hXHhX/AKE3w1/4Cxf/ABFH9n/9Pvw/4Ic3kalFZf8Awrjwr/0Jvhr/AMBYv/iKP+FceFf+hN8Nf+AsX/xFH9n/APT78P8AghzeRqUVl/8ACuPCv/Qm+Gv/AAFi/wDiKP8AhXHhX/oTfDX/AICxf/EUf2f/ANPvw/4Ic3kalFZf/CuPCv8A0Jvhr/wFi/8AiKP+FceFf+hN8Nf+AsX/AMRR/Z//AE+/D/ghzeRqUVl/8K48K/8AQm+Gv/AWL/4ij/hXHhX/AKE3w1/4Cxf/ABFH9n/9Pvw/4Ic3kalFZf8Awrjwr/0Jvhr/AMBYv/iKP+FceFf+hN8Nf+AsX/xFH9n/APT78P8AghzeRqUVl/8ACuPCv/Qm+Gv/AAFi/wDiKP8AhXHhX/oTfDX/AICxf/EUf2f/ANPvw/4Ic3kalFZf/CuPCv8A0Jvhr/wFi/8AiKP+FceFf+hN8Nf+AsX/AMRR/Z//AE+/D/ghzeRBrf8Ax8WX/Xw//ppu66esex8FaBauH03wzolrMM+TcwW8atFuUg7JFUH5lJHWtivQw1KytzX6kNhRRRW4gooooAKKKKACiiigAooooAKKKKACiiigDmJPDWsxzTPpOuaTDHK637wXNo7mGRLWJMLMlzHwwiB+53p/9k+KP+hi8Of+AEv/AMmV0lFYSoQfxU492O/mc3/ZPij/AKGLw5/4AS//ACZR/ZPij/oYvDn/AIAS/wDyZXSUVP1aH/PqIXfc5v8AsnxR/wBDF4c/8AJf/kyj+yfFH/QxeHP/AAAl/wDkyukoo+rQ/wCfUQu+5zf9k+KP+hi8Of8AgBL/APJlH9k+KP8AoYvDn/gBL/8AJldJRR9Wh/z6iF33Ob/snxR/0MXhz/wAl/8AkysLXNX8RWMsKS6zoMhlO1nWxk/0eJrmGMEA3nJ8+4j44+Xcc5AVvQa4xdMi1Nr03LyLBKD4ShliwC9lZJIrFQwOGju5Z1yRghQQCOWxxFOmld0o9kNN9y//AGT4o/6GLw5/4AS//JlH9k+KP+hi8Of+AEv/AMmVoeG9Wlu7eN7tIknx9n1OKMHbb6jbsVkWPJPEM6so+ZgcZBI5rUrZYen0pRFd9zm/7J8Uf9DF4c/8AJf/AJMo/snxR/0MXhz/AMAJf/kyukoo+rQ/59RC77nN/wBk+KP+hi8Of+AEv/yZR/ZPij/oYvDn/gBL/wDJldJRR9Wh/wA+ohd9zm/7J8Uf9DF4c/8AACX/AOTKP7J8Uf8AQxeHP/ACX/5MrpKKPq0P+fUQu+5zf9k+KP8AoYvDn/gBL/8AJlamg6R9it4YvP8AO8qOPTvPxjz/ALPEBnZk434z1OPWtCitadOK/hxS7g33CiiitBBRRRQB8QeO/wDkI3n/AF8z/wDpS9c/XQeO/wDkI3n/AF8z/wDpS9c/QB9v+BP+QdZ/9e0H/pMldBXP+BP+QdZ/9e0H/pMldBQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFcDceF72wa1j07xl4gWKSVrGfelqW2/Y7iQt5htslpZYwSWLFsknJOa76uf8Q/6+x/6+X/9NN5Uygn8cYvqg9C5oXh5bBXC6jfXe52v3kuNg8uSUDIhjgRFAdgWOE5Yljkk1qUUU0uy8kAUUUUwCiiigAooooAKKKKACiiigAooooA+IPHf/IRvP+vmf/0peufroPHf/IRvP+vmf/0peufoA+3/AAJ/yDrP/r2g/wDSZK6Cuf8AAn/IOs/+vaD/ANJkroKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigArn/EP+vsf+vl//AE03ldBXP+If9fY/9fL/APppvKAOgooooAKKKKACiiigAooooAKKKKACiiigAooooA+IPHf/ACEbz/r5n/8ASl65+ug8d/8AIRvP+vmf/wBKXrn6APt/wJ/yDrP/AK9oP/SZK6Cuf8Cf8g6z/wCvaD/0mSugoAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACuf8AEP8Ar7H/AK+X/wDTTeV0Fc/4h/19j/18v/6abygDoKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPiDx3/AMhG8/6+Z/8A0peufroPHf8AyEbz/r5n/wDSl65+gD7f8Cf8g6z/AOvaD/0mSugrn/An/IOs/wDr2g/9JkroKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAPPm0Pw7JPfSeJdL0KQLOkTXt9HGfIi/su0wDNKOBub1xk1rD4eeF+3hDw56g/ZYuf/AB2uO+IH/Hnq3/XeH/0isK0/Evi6zmSEaX4itTaeakXiS7spxmws3U48+4ibKLNKACcr6ZGa8uph5PWnOespUpdkkov73eyXVml+68zf/wCFd+F/+hP8Of8AgLF/8TVHU/DHgqzCf2j4X8ORB3XTbU/ZEPm3U+cD5UP3sdTgeprjpNdusYGt3w0X7YLNNZ85stp3lZKjVN2dgufl3eZ/s7gKZba7P5bf2drt9PCNTi060uBcO/mac3l/ItwzMSr5PG4g1UMDL7daX5fJvWz7qxLn5Lr+F/8AI9C/4V34X/6E/wAOf+AsX/xNZi6N8PjJ5aab4GNxn7P9jEcG8TKfu+TjOQe2M1yRvr5UeVdZ1fzl1H+yYlM77EsGuFGw2pbaRgnqpI7HFV7nUNOa11GGWa3mvHup10vTFIMs1y7LtMFuMn5XHXGBjrVU8C/tVqj1VPTp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&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;400&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;ViewModel構造&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
メインウィンドウ用としてMainViewModelがあり、ログインダイアログ用としてLoginDialogViewModelがあります。そして、MainViewModelはDialogプロパティによって（ViewModelBaseを通して間接的に）LoginDialogViewModelを保持します。&lt;br /&gt;
&lt;br /&gt;
このDialog 
プロパティの多重度が「0,1」となっているところがポイントです。これは、ダイアログが表示されているかどうかを、DialogViewModelのイ
ンスタンスが存在するか、Nullかによって判断するというモデルになっています。つまり、ダイアログ表示のためには、Dialogプロパティに
DialogViewModelのインスタンスをセットし、またダイアログを閉じるにはNullをセットすれば良いことになります。&lt;br /&gt;
&lt;br /&gt;
このように、ViewModelの状態変更によってダイアログ表示を制御できるのは、ダイアログ表示状態そのものがViewModelのクラス構造の中にモデル化されているからです。ここはMVVM Dialog Behaviorの思想がよく表れている部分です。&lt;br /&gt;
&lt;br /&gt;
それではViewModelのコードを見てみましょう。まずは、MainViewModelのソースコードを見てみます。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MainViewModelのソースコード&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;

namespace MvvmDialogBehaviorSample
{
    public class MainViewModel:ViewModelBase
    {
        public MainViewModel()
        {
        }

        private ViewModelBase _Dialog;
        public ViewModelBase Dialog
        {
            get { return _Dialog; }
            set
            {
                if (_Dialog != value)
                {
                    _Dialog = value;
                    RaisePropertyChanged(&quot;Dialog&quot;);
                }
            }
        }

        private ICommand _OpenDialogCommand;
        public ICommand OpenDialogCommand
        {
            get { 
                if (_OpenDialogCommand == null)
                {
                    _OpenDialogCommand = new RelayCommand(OpenDialog);
                }

                return _OpenDialogCommand;
            }
        }

        private void OpenDialog()
        {
            &lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;// Open Dialog&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
            &lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;Dialog = new LoginDialogViewModel(CloseDialog) { UserName=&quot;LastLoginUser1&quot; };&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
        }

        private void CloseDialog()
        {
            &lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;// Close Dialog&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
            &lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;Dialog = null;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
        }
    }
}&lt;/span&gt;　&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
注目すべきは、ダイアログオープンのために、Dialogプロパティへのインスタンスのセットしか行っていないことです。そして、CloseDialog()メソッドにて、ダイアログクローズのために、DialogプロパティにNullのセットしか行っていません。&lt;br /&gt;
&lt;br /&gt;
また、ダイアログに引き渡すべきユーザ名がViewModelプロパティによる単純な受け渡しで実現できています。&lt;br /&gt;
&amp;nbsp; &lt;br /&gt;
ところで、ここで問題になるのが、そもそもどうやってこのCloseDialog（）メソッドを呼び出すのかということです。ここでは、ダイアログ側か
らのクローズを考慮して、このメソッド自体をLoginDialogViewModelのコンストラクタに渡してコールバック可能にしていることに注意してくださ
い。このあたりの実装方法は、他にもいろいろ考えられます（＊１）ので、それぞれのプロジェクトに合わせて実装していただければと思います。&lt;br /&gt;
&lt;br /&gt;
＊１：ほかの実装方法としては例えば、ダイアログViewModelにクローズイベントを作成して、ダイアログの呼び出し元ViewModelからイベントハンドラとしてをCloseDialog()をあらかじめ登録しておき、ダイアログViewModel側でクローズしたいときにイベントを発火させてクローズさせる方法も考えられます。 &lt;br /&gt;
&lt;br /&gt;
次に、LoginDialogViewModelのソースコードを見てみます。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LoginDialogViewModelのソースコード&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;
using System.Windows;

namespace MvvmDialogBehaviorSample
{
    public class LoginDialogViewModel:ViewModelBase
    {
        private Action _CloseAction;

        public LoginDialogViewModel(Action closeAction)
        {
            _CloseAction = closeAction;
        }

        private String _UserName;
        public String UserName
        {
            get { return _UserName; }
            set
            {
                if (_UserName != value)
                {
                    _UserName = value;
                    RaisePropertyChanged(&quot;UserName&quot;);
                }
            }
        }

        private String _Password;
        public String Password
        {
            get { return _Password; }
            set
            {
                if (_Password != value)
                {
                    _Password = value;
                    RaisePropertyChanged(&quot;Password&quot;);
                }
            }
        }

        private ICommand _LoginCommand;
        public ICommand LoginCommand
        {
            get
            {
                if (_LoginCommand == null)
                {
                    _LoginCommand = new RelayCommand(Login);
                }

                return _LoginCommand;
            }
        }

        private void Login()
        {
            if (DoLogin())
            {
                _CloseAction();
            }
        }

        private ICommand _CancelCommand;
        public ICommand CancelCommand
        {
            get
            {
                if (_CancelCommand == null)
                {
                    _CancelCommand = new RelayCommand(Cancel);
                }

                return _CancelCommand;
            }
        }

        private void Cancel()
        {
            _CloseAction();
        }

        private bool DoLogin()
        {
            // Login Process...
            return true;
        }
    }
}&lt;/span&gt;　&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
ログインダイアログとして、ユーザ名とパスワードのプロパティを持ち、またログインとキャンセルのコマンドを持っています。たいしたコードでもありませんが、先ほど説明したとおり、自身のダイアロ
 
グをクローズするために、MainViewModelから渡されたCloseDialog()メソッドをコールバックしている点はご注意くだ
 さい。&lt;br /&gt;
&lt;br /&gt;
これで、ViewModel側のソースコードの説明は終了です。次に、View側のソースコードを見てみます。&lt;br /&gt;
&lt;br /&gt;
MainWindowのソースコード&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;lt;Window x:Class=&quot;MvvmDialogBehaviorSample.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:local=&quot;clr-namespace:MvvmDialogBehaviorSample&quot;
        xmlns:i=&quot;http://schemas.microsoft.com/expression/2010/interactivity&quot;
        xmlns:cmd=&quot;clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4&quot;
        xmlns:mvvmdialogbehavor=&quot;clr-namespace:MvvmDialogBehavior;assembly=MvvmDialogBehavior&quot;
        Title=&quot;MainWindow&quot; Height=&quot;275&quot; Width=&quot;375&quot; Background=&quot;Azure&quot;&amp;gt;
    &amp;lt;Window.DataContext&amp;gt;
        &amp;lt;local:MainViewModel /&amp;gt;
    &amp;lt;/Window.DataContext&amp;gt;

    &amp;lt;i:Interaction.Behaviors&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;        &amp;lt;mvvmdialogbehavor:DialogBehavior Content=&quot;{Binding Dialog}&quot; &amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
            
&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;            &amp;lt;mvvmdialogbehavor:DialogBehavior.Resources &amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
                &amp;lt;DataTemplate DataType=&quot;{x:Type local:LoginDialogViewModel}&quot; &amp;gt;
                    &amp;lt;Grid Height=&quot;250&quot; Width=&quot;350&quot; Background=&quot;Ivory&quot;&amp;gt;
                        &amp;lt;Label Content=&quot;LoginDialogView&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Name=&quot;label1&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
                        &amp;lt;Label Content=&quot;UserName&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,70,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
                        &amp;lt;Label Content=&quot;Password&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,112,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
                        &amp;lt;TextBox Text=&quot;{Binding UserName}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,72,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
                        &amp;lt;TextBox Text=&quot;{Binding Password}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,114,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
                        &amp;lt;Button Content=&quot;Login&quot; Command=&quot;{Binding LoginCommand}&quot; Margin=&quot;61,157,189,39&quot; /&amp;gt;
                        &amp;lt;Button Content=&quot;Cancel&quot; Command=&quot;{Binding CancelCommand}&quot; Margin=&quot;167,157,83,39&quot; /&amp;gt;
                    &amp;lt;/Grid&amp;gt;
                &amp;lt;/DataTemplate&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;            &amp;lt;/mvvmdialogbehavor:DialogBehavior.Resources&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
            
&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;            &amp;lt;mvvmdialogbehavor:DialogBehavior.Style&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
                &amp;lt;Style TargetType=&quot;Window&quot;&amp;gt;
                    &amp;lt;Setter Property=&quot;SizeToContent&quot; Value=&quot;WidthAndHeight&quot;/&amp;gt;
                &amp;lt;/Style&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;            &amp;lt;/mvvmdialogbehavor:DialogBehavior.Style&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
            
&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;        &amp;lt;/mvvmdialogbehavor:DialogBehavior&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
    &amp;lt;/i:Interaction.Behaviors&amp;gt;
    
    &amp;lt;Grid&amp;gt;
            &amp;lt;Button Content=&quot;Open Dialog&quot; Command=&quot;{Binding OpenDialogCommand}&quot; Height=&quot;49&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;88,83,0,0&quot; Name=&quot;button1&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;160&quot; /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;　&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
このMainWindowのコードには、メインウィンドウとログインダイアログのViewの定義の両方が記述されています。また、ログインダイアログの定義では、MVVM Dialog Behaviorが提供するDialogBehaviorクラスが使用されています。&lt;br /&gt;
&lt;br /&gt;
ここで使用されているDialogBehaviorクラスのプロパティである、Content、Resources、Styleは、実は意図的にWindowクラスが持つプロパティと同名となっています。そしてこれらは、ダイアログの実体であるWPFのWindowインスタンスのプロパティにそのまま適用されることになります。&lt;br /&gt;
&lt;br /&gt;
ただし実は、Contentプロパティだけは少し特別な扱いを受けています。通常は、ここにダイアログViewModelをバインディングするようにして使用するのですが、このダイアログのViewModelが存在している間のみダイアログが表示され、Nullの時にはダイアログは非表示となるようになっているのです。&lt;br /&gt;
&lt;br /&gt;
これは、ViewModel側でダイアログが表示されるかどうかを、ダイアログのViewModelのインスタンスが存在するかNullかで表現するとした、ViewModelの定義とうまく適合するようになっています。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
さて、先ほども説明したとおり、DialogBehaviorビヘイビアのResourcesとStyleのプロパティは、そのままダイアログのWindowインスタンスのプロパティへと設定されます。それ以上の働きはしませんが、サンプルコードの内容については少し説明しておきましょう。&lt;span style=&quot;font-size: small;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回のサンプルのResourcesプロパティにはDataTemplateが指定されています。このDataTemplateは、ViewModelとViewをマッピングする役割があります。DataTypeプロパティにViewModelの型を指定して、さらにViewを記述するのです。このようにしておくと、ContentプロパティのViewModelの型によって自動的にViewが適用され、またViewとViewModel間のDataContextの設定も自動的に行われます。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回のサンプルコードでは、&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;LoginDialogViewModelインスタンスがContentプロパティに設定されたときに、Grid以下のコントロールがViewとして適用されることになります。&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
次に、 Styleプロパティでは、WindowのSizeToContentプロパティにWidthAndHeightを指定しています。このため、内部のViewの縦横の幅に合わせてダイアログのWindowの縦横の幅となります。&lt;br /&gt;
&lt;br /&gt;
以上でサンプルの説明は終了です。シンプルにダイアログ表示が実現できていると思いませんか。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□もっと実践的にMVVM Dialog Behaviorを使う　１　（Viewの共通化）&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
比較的単純にMVVM Dialog Behaviorを使用するサンプルコードをご説明してきました。しかし、実際のプロジェクトでは、もっとコードの共通化と再利用を推し進めることで、MVVM Dialog Behaviorに指定するコードはさらにシンプルなものになります。&lt;br /&gt;
&lt;br /&gt;
まず、 ログインダイアログのViewを共通化するところから見ていきましょう。&lt;br /&gt;
&lt;br /&gt;
現在は、ログインダイアログのViewがDataTemplate内にインライン記述されています。しかし、複数の場所からログインダイアログを使用したい場合に、それぞれの場所でインラインに記述していては、コードの重複が発生することになるでしょう。&lt;br /&gt;
&lt;br /&gt;
この問題を解決するためにログインダイアログのViewを共通化しましょう。といってもViewの共通化とは、単純にLoginDialogView.xamlファイルを作成して、DataTemplate内に記述していたXAMLコードを移動するだけです。&lt;br /&gt;
&lt;br /&gt;
LoginDialogView.xamlの内容は以下のようになります。&lt;br /&gt;
&lt;br /&gt;
LoginDialogViewのソースコード&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;lt;UserControl x:Class=&quot;MvvmDialogBehaviorSample.LoginDialogView&quot;
             xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot; 
             xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot; 
             xmlns:i=&quot;http://schemas.microsoft.com/expression/2010/interactivity&quot;
             xmlns:cmd=&quot;clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4&quot;
             xmlns:mvvmdialogbehavor=&quot;clr-namespace:&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Mvvm&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;DialogBehavior;assembly=MvvmDialogBehavior&quot;
             mc:Ignorable=&quot;d&quot; 
             d:DesignHeight=&quot;238&quot; d:DesignWidth=&quot;338&quot;  &amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
    &amp;lt;Grid Height=&quot;250&quot; Width=&quot;350&quot; Background=&quot;Ivory&quot;&amp;gt;
        &amp;lt;Label Content=&quot;LoginDialogView&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Name=&quot;label1&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
        &amp;lt;Label Content=&quot;UserName&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,70,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
        &amp;lt;Label Content=&quot;Password&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,112,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
        &amp;lt;TextBox Text=&quot;{Binding UserName}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,72,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
        &amp;lt;TextBox Text=&quot;{Binding Password}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,114,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;Login&quot; Command=&quot;{Binding LoginCommand}&quot; Margin=&quot;61,157,189,39&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;Cancel&quot; Command=&quot;{Binding CancelCommand}&quot; Margin=&quot;167,157,83,39&quot; /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/UserControl&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt; &lt;/span&gt; &lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
これに伴って、MainWindowのDataTemplateは以下のような記述に変更することになるでしょう。&lt;br /&gt;
&lt;br /&gt;
MainWindowのソースコードの抜粋（LoginDialogView.xaml適用）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;    &amp;lt;i:Interaction.Behaviors&amp;gt;
        &amp;lt;mvvmdialogbehavor:DialogBehavior Content=&quot;{Binding Dialog}&quot; &amp;gt;
            
            &amp;lt;mvvmdialogbehavor:DialogBehavior.Resources &amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;                &amp;lt;DataTemplate DataType=&quot;{x:Type local:LoginDialogViewModel}&quot; &amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;                    &amp;lt;local:LoginDialogView /&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;                &amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
            &amp;lt;/mvvmdialogbehavor:DialogBehavior.Resources&amp;gt;
            
            &amp;lt;mvvmdialogbehavor:DialogBehavior.Style&amp;gt;
                &amp;lt;Style TargetType=&quot;Window&quot;&amp;gt;
                    &amp;lt;Setter Property=&quot;SizeToContent&quot; Value=&quot;WidthAndHeight&quot;/&amp;gt;
                &amp;lt;/Style&amp;gt;
            &amp;lt;/mvvmdialogbehavor:DialogBehavior.Style&amp;gt;
            
        &amp;lt;/mvvmdialogbehavor:DialogBehavior&amp;gt;
    &amp;lt;/i:Interaction.Behaviors&amp;gt; &lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
これで少しDialogBehavior内の記述が簡単になりました。 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□もっと実践的にMVVM Dialog Behaviorを使う　２　（App.xamlへDataTemplate移動）&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
次に、このDataTemplateによるViewModelとViewのマッピングは、システム全体に対して１つあれば事足りることが多いでしょう。そうなると、このDataTemplateによるマッピングはApp.xamlに記述してしまえば良いことになります。&lt;br /&gt;
&lt;br /&gt;
App.xamlのソースコード&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;lt;Application x:Class=&quot;MvvmDialogBehaviorSample.App&quot;
             xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:local=&quot;clr-namespace:MvvmDialogBehaviorSample&quot;
             StartupUri=&quot;MainWindow.xaml&quot;&amp;gt;
    &amp;lt;Application.Resources&amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;        &amp;lt;DataTemplate DataType=&quot;{x:Type local:LoginDialogViewModel}&quot;&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;            &amp;lt;local:LoginDialogView /&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;        &amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &amp;lt;/Application.Resources&amp;gt;
&amp;lt;/Application&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;　&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
これに伴って、MainWindowのDialogBehaviorからResourcesの記述も不要となります。&lt;br /&gt;
&lt;br /&gt;
MainWindowのソースコードの抜粋（App.xamlへDataTemplate移動）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;    &amp;lt;i:Interaction.Behaviors&amp;gt;
        &amp;lt;mvvmdialogbehavor:DialogBehavior Content=&quot;{Binding Dialog}&quot; &amp;gt;
            
            &amp;lt;mvvmdialogbehavor:DialogBehavior.Style&amp;gt;
                &amp;lt;Style TargetType=&quot;Window&quot;&amp;gt;
                    &amp;lt;Setter Property=&quot;SizeToContent&quot; Value=&quot;WidthAndHeight&quot;/&amp;gt;
                &amp;lt;/Style&amp;gt;
            &amp;lt;/mvvmdialogbehavor:DialogBehavior.Style&amp;gt;
            
        &amp;lt;/mvvmdialogbehavor:DialogBehavior&amp;gt;
    &amp;lt;/i:Interaction.Behaviors&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□もっと実践的にMVVM Dialog Behaviorを使う　３　（WindowのStyleの移動）&lt;/b&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;br /&gt;
これでかなりDialogBehaviorも単純になりましたが、よくよく考えるとStyleのプロパティも、それぞれのダイアログ種別に応じて指定するものであるため、MainWindow.xaml内ではなく、LoginDialogView.xaml内に記述できたほうがよさそうです。&lt;br /&gt;
&lt;br /&gt;
こうしたことから、MVVM Dialog Behaviorでは、「WindowStyleBehavior」クラスを提供しています。これを使用すると、LoginDialogView.xamlは以下のようなコードとなります。&lt;br /&gt;
&lt;br /&gt;
LoginDialogViewのソースコード（WindowStyleBehavior適用）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;lt;UserControl x:Class=&quot;MvvmDialogBehaviorSample.LoginDialogView&quot;
             xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot; 
             xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot; 
             xmlns:i=&quot;http://schemas.microsoft.com/expression/2010/interactivity&quot;
             xmlns:cmd=&quot;clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4&quot;
             xmlns:mvvmdialogbehavor=&quot;clr-namespace:&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Mvvm&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;DialogBehavior;assembly=MvvmDialogBehavior&quot;
             mc:Ignorable=&quot;d&quot; 
             d:DesignHeight=&quot;238&quot; d:DesignWidth=&quot;338&quot;  &amp;gt;
    
    &amp;lt;i:Interaction.Behaviors&amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;        &amp;lt;mvvmdialogbehavor:WindowStyleBehavior&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;            &amp;lt;mvvmdialogbehavor:WindowStyleBehavior.Style&amp;gt;&lt;/span&gt;
                &amp;lt;Style TargetType=&quot;Window&quot;&amp;gt;
                    &amp;lt;Setter Property=&quot;SizeToContent&quot; Value=&quot;WidthAndHeight&quot;/&amp;gt;
                &amp;lt;/Style&amp;gt;            
            &amp;lt;/mvvmdialogbehavor:WindowStyleBehavior.Style&amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;        &amp;lt;/mvvmdialogbehavor:WindowStyleBehavior&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;    &amp;lt;/i:Interaction.Behaviors&amp;gt;&lt;/span&gt;

    &amp;lt;Grid Height=&quot;250&quot; Width=&quot;350&quot; Background=&quot;Ivory&quot;&amp;gt;
        &amp;lt;Label Content=&quot;LoginDialogView&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Name=&quot;label1&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
        &amp;lt;Label Content=&quot;UserName&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,70,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
        &amp;lt;Label Content=&quot;Password&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,112,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
        &amp;lt;TextBox Text=&quot;{Binding UserName}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,72,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
        &amp;lt;TextBox Text=&quot;{Binding Password}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,114,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;Login&quot; Command=&quot;{Binding LoginCommand}&quot; Margin=&quot;61,157,189,39&quot; /&amp;gt;
        &amp;lt;Button Content=&quot;Cancel&quot; Command=&quot;{Binding CancelCommand}&quot; Margin=&quot;167,157,83,39&quot; /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/UserControl&amp;gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt; &lt;/span&gt; &lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
WindowStyleBehaviorは、そのStyleプロパティの値で、自身のコントロールを保持するWindowのStyleを上書きするビヘイビアです。このビヘイビアにより、ダイアログのWindowのStyleをViewの中から設定することができます。&lt;br /&gt;
&lt;br /&gt;
これで、DialogBehaviorでのStyleの記述も不要となりますので、結果的にMainWindowのDialogBehaviorにはContentプロパティしか残らないことになります。&lt;br /&gt;
&lt;br /&gt;
MainWindowのソースコードの抜粋（WindowStyleの移動）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;    &amp;lt;i:Interaction.Behaviors&amp;gt;
        &amp;lt;mvvmdialogbehavor:DialogBehavior Content=&quot;{Binding Dialog}&quot; /&amp;gt;
    &amp;lt;/i:Interaction.Behaviors&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt; &lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Contentプロパティしか存在しないということは、MainWindowにはログインダイアログといった個別のダイアログに関する設定は含まれないということでもあります。これによってMainViewModelのDialogプロパティにセットするViewModelクラスの種類によって、ダイアログ種別を柔軟に切り替えることができるようになるのです。&lt;br /&gt;
&lt;br /&gt;
実際のプロジェクトでは、この方法を全体的に使用していくのが良いでしょう。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□もっと実践的にMVVM Dialog Behaviorを使う　番外編　（WindowのStyleへのデータバインディング）&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
今までのWindowのStyleは静的に値を与えていましたが、これをダイアログのViewModelのプロパティとのデータバインディングによって動的に制御したい場合があるでしょう。その場合には以下のようにRelativeSourceを使用することで実現できます。&lt;br /&gt;
&lt;br /&gt;
この例では、WindowのTitleプロパティに、ViewModelの
WindowTitleプロパティをバインディングしています。&lt;br /&gt;
&lt;br /&gt;
LoginDialogViewのソースコード（WindowのStyleにViewModelのプロパティをバインディング）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;color: black; font-size: x-small;&quot;&gt;        &amp;lt;mvvmdialogbehavor:WindowStyleBehavior&amp;gt;
            &amp;lt;mvvmdialogbehavor:WindowStyleBehavior.Style&amp;gt;
                &amp;lt;Style TargetType=&quot;Window&quot;&amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;                    &amp;lt;Setter Property=&quot;Title&quot; Value=&quot;{Binding Path=Content.WindowTitle, &lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;                        RelativeSource={RelativeSource Mode=Self}}&quot;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span style=&quot;color: black; font-size: x-small;&quot;&gt;                &amp;lt;/Style&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color: black; font-size: x-small;&quot;&gt;            &amp;lt;/mvvmdialogbehavor:WindowStyleBehavior.Style&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color: black; font-size: x-small;&quot;&gt;        &amp;lt;/mvvmdialogbehavor:WindowStyleBehavior&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&amp;nbsp;&lt;/span&gt; &lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
{RelativeSource Mode=Self}はダイアログのWindowインスタンスを表現します。そして、WindowのContentにはダイアログのViewModelが設定されていることを思い出してください。このため、Pathでは、Contentに続けてダイアログのViewModelのプロパティ名を記述することで、データバインドを行うことができます。&lt;br /&gt;
&lt;br /&gt;
さて、既にもうほとんどのMVVM Dialog Behaviorの機能を使いながら紹介しました。あと２つだけ紹介していない機能があります。次にこれらについて説明していきます。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□その他のMVVM Dialog Behaviorの機能 １　(IsModalプロパティ)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
DialogBehaviorのIsModalプロパティは、モーダルにダイアログを表示するかをboolで指定します。デフォルトではモーダル（True）です。&lt;br /&gt;
&lt;br /&gt;
MainWindowのソースコードの抜粋（IsModalを使用）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;    &amp;lt;i:Interaction.Behaviors&amp;gt;
        &amp;lt;mvvmdialogbehavor:DialogBehavior Content=&quot;{Binding Dialog}&quot; &lt;span style=&quot;color: red;&quot;&gt;IsModal=&quot;False&quot;&lt;/span&gt; /&amp;gt;
    &amp;lt;/i:Interaction.Behaviors&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt; &lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□その他のMVVM Dialog Behaviorの機能 ２　(ContentTemplateプロパティ)&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
また、DialogBehaviorのContentTemplateプロパティは、ResourecesにDataTemplateを指定するよりも直接的にダイアログのViewを指定することができます。ContentTemplateプロパティのDataTemplateでは、マッピング対象の型に関わらず適用される型(DataTypeプロパティ)の指定が不要です。このため、ダイアログの見た目(View)を固定で決め打ちする場合に適しています。&lt;br /&gt;
&lt;br /&gt;
MainWindowのソースコードの抜粋（LoginDialogView.xaml適用）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;    &amp;lt;i:Interaction.Behaviors&amp;gt;
        &amp;lt;mvvmdialogbehavor:DialogBehavior Content=&quot;{Binding Dialog}&quot; &amp;gt;
            
&lt;span style=&quot;color: red;&quot;&gt;            &amp;lt;mvvmdialogbehavor:DialogBehavior.ContentTemplate&amp;gt;
&lt;/span&gt;&lt;span style=&quot;color: red;&quot;&gt;                &amp;lt;DataTemplate&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color: red;&quot;&gt;                    &amp;lt;local:LoginDialogView /&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;
&lt;/span&gt;&lt;span style=&quot;color: red;&quot;&gt;                &amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: black;&quot;&gt;
&lt;span style=&quot;color: red;&quot;&gt;            &amp;lt;/mvvmdialogbehavor:DialogBehavior.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;ContentTemplate&lt;/span&gt;&lt;span style=&quot;color: red; font-size: x-small;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;
        &amp;lt;/mvvmdialogbehavor:DialogBehavior&amp;gt;
    &amp;lt;/i:Interaction.Behaviors&amp;gt;&lt;/span&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt; &lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
以上で、MVVM Dialog Behaviorの機能の説明はひととおり終了です。後はいくつか補足をしてMVVM Dialog Behaviorの説明を終了としましょう。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[補足]ラベルやボタンでのコンテンツの表示と、ダイアログ表示とで、同じプログラミング体験を提供します&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
DataTemplateにあまりなじみがない方には、DialogBehaviorビヘイビアのこの使用方法はちょっと難しかったかもしれません。しかし、これはラベルやボタン（ContentControlを継承している）のでのコンテンツ表示と同じプログラミング体験を提供するという目的によるものです。&lt;br /&gt;
&lt;br /&gt;
実は、DialogBehaviorビヘイビアをContentControl置き換えて（＋さらにほんの少しだけ修正する必要がありますが。）も、そのまま動作します。&lt;br /&gt;
&lt;br /&gt;
この場合、メインウィンドウのOpenDialogボタンを押すと、なんと以下のようにメインウィンドウ内にログインダイアログが出現します。もちろんLoginボタンを押すと元通りに消えるというダイアログらしさもそのままです。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; height=&quot;291&quot; src=&quot;data:image/png;base64,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&quot; style=&quot;margin-left: auto; margin-right: auto;&quot; width=&quot;400&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;メインウィンドウ（ContentControl版）&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;br /&gt;
このContentControlによるコンテンツの表示は、MVVMではよく知られたテクニックの一つです。（知らなかった方はこの機会に是非慣れ親しんでください。）&lt;br /&gt;
&lt;br /&gt;
このように、MVVM Dialog Behaviorでは、ラベルやボタン（ContentControlを継承している）のコンテンツ表示と、ダイアログ表示とが同様のプログラミング体験となるように、そのインタフェースが設計されているのです。&lt;br /&gt;
&lt;br /&gt;
ちなみに、以下のようにMainWindowのソースコードを修正すると上記ことが実現できます。&lt;br /&gt;
&lt;br /&gt;
MainWindowのソースコード（ContentControl版）&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;span style=&quot;font-size: xx-small;&quot;&gt;&amp;lt;Window x:Class=&quot;MvvmDialogBehaviorSample.MainWindow&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:local=&quot;clr-namespace:MvvmDialogBehaviorSample&quot;
        xmlns:i=&quot;http://schemas.microsoft.com/expression/2010/interactivity&quot;
        xmlns:cmd=&quot;clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4&quot;
        xmlns:mvvmdialogbehavor=&quot;clr-namespace:MvvmDialogBehavior;assembly=MvvmDialogBehavior&quot;
        Title=&quot;MainWindow&quot; Height=&quot;275&quot; Width=&quot;375&quot; Background=&quot;Azure&quot;&amp;gt;
    &amp;lt;Window.DataContext&amp;gt;
        &amp;lt;local:MainViewModel /&amp;gt;
    &amp;lt;/Window.DataContext&amp;gt;

    &amp;lt;Grid&amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;        &amp;lt;ContentControl Content=&quot;{Binding Dialog}&quot;&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;            &amp;lt;ContentControl.Resources&amp;gt;&lt;/span&gt;
                &amp;lt;DataTemplate DataType=&quot;{x:Type local:LoginDialogViewModel}&quot; &amp;gt;
                    &amp;lt;Grid Height=&quot;250&quot; Width=&quot;350&quot; Background=&quot;Ivory&quot;&amp;gt;
                        &amp;lt;Label Content=&quot;LoginDialogView&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Name=&quot;label1&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
                        &amp;lt;Label Content=&quot;UserName&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,70,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
                        &amp;lt;Label Content=&quot;Password&quot; Height=&quot;29&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;22,112,0,0&quot; VerticalAlignment=&quot;Top&quot; /&amp;gt;
                        &amp;lt;TextBox Text=&quot;{Binding UserName}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,72,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
                        &amp;lt;TextBox Text=&quot;{Binding Password}&quot; Height=&quot;24&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;96,114,0,0&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;120&quot; /&amp;gt;
                        &amp;lt;Button Content=&quot;Login&quot; Command=&quot;{Binding LoginCommand}&quot; Margin=&quot;61,157,189,39&quot; /&amp;gt;
                        &amp;lt;Button Content=&quot;Cancel&quot; Command=&quot;{Binding CancelCommand}&quot; Margin=&quot;167,157,83,39&quot; /&amp;gt;
                    &amp;lt;/Grid&amp;gt;
                &amp;lt;/DataTemplate&amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;            &amp;lt;/ContentControl.Resources&amp;gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;        &amp;lt;/ContentControl&amp;gt;&lt;/span&gt;

        &amp;lt;Button Content=&quot;Open Dialog&quot; Command=&quot;{Binding OpenDialogCommand}&quot; Height=&quot;49&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;88,83,0,0&quot; Name=&quot;button1&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;160&quot; /&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;&lt;/span&gt;　&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[補足]ダイアログの閉じるボタンを押されたらどうなる？&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ダイアログには、何も指定しないと右上に閉じるボタンが表示されます。このボタンを押した場合、DialogBehaviorはどのような振る舞いをするのでしょうか？&lt;br /&gt;
&lt;br /&gt;
実は、ダイアログが閉じられるとDialogBehaviorのContentプロパティはNullに設定されます。このContentプロパティにダイアログViewModelのプロパティがバインドされるでしょうから、バインディング機能により、ダイアログViewModelへプロパティも同時にNullに設定されることになります。&lt;br /&gt;
&lt;br /&gt;
実はこの仕組みによって、ダイアログViewModelインスタンスが存在するかNullかでダイアログの表示中かどうかが判断できるように、常にViewModel側の状態に同期されることになるのです。&lt;br /&gt;
&lt;br /&gt;
なお、Alt+F4などのウィンドウを閉じるショートカットキーが押された場合でも同様の動きをします。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[補足]メッセンジャーによるダイアログ表示との使い分け&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
メッセンジャーによるダイアログ表示はMVVMに反するところがあると述べましたが、実際のところ、OKやCancelボタンがあるだけのような単純なメッ
セージボックスや、ファイル選択のダイアログなどであれば、そもそもViewModelで制御したいとも思わないかもしれません。&lt;br /&gt;
&lt;br /&gt;
そうした場合には、
MVVM Dialog 
Behaviorを使用する必要はなく、何れかのMVVMフレームワークが提供するメッセージボックスのためのメッセンジャー（およびメッセージやアク
ション）を使用するのも良いでしょう。&lt;br /&gt;
&lt;br /&gt;
しかし、ある程度の複雑なプレゼンテーションロジックを持ったダイアログを表示する場合や、ダイアログ表示中かどうかをViewModel内で判断してその表示を制御したいなどの場合、あるいはダイアログのテーマを自由にデザインしたい場合には、このMVVM Dialog Behaviorの使用を検討する価値があります。&lt;br /&gt;
&lt;br /&gt;
プロジェクト要件に応じて使い分けるようにしてください。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□まとめ&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
これで、&lt;span style=&quot;font-size: small;&quot;&gt;MVVMでメッセンジャーなしにダイアログ&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;表示をシンプルに&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;実現させるMVVM Dialog Behaviorライブラリのご紹介は終了です。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
メッセンジャーによるダイアログ表示に疑問を持ったらこのライブラリを使ってみることを検討してください。このライブラリが少しでも皆様のMVVMライフの足しになれば幸いです。&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&amp;nbsp;&lt;/span&gt; &lt;br /&gt;
長文お疲れ様でした。&lt;br /&gt;
&lt;br /&gt;
以上。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/597055148556121877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/597055148556121877'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2011/12/mvvmmvvm-dialog-behavior.html' title='MVVMでメッセンジャーを使わずにダイアログを表示する(MVVM Dialog Behaviorライブラリ提供)'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-2160655483358506048</id><published>2011-12-05T21:26:00.001+09:00</published><updated>2012-10-17T11:26:29.865+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="messenger"/><category scheme="http://www.blogger.com/atom/ns#" term="mvvm"/><category scheme="http://www.blogger.com/atom/ns#" term="navigation"/><category scheme="http://www.blogger.com/atom/ns#" term="presentation model"/><category scheme="http://www.blogger.com/atom/ns#" term="transition"/><category scheme="http://www.blogger.com/atom/ns#" term="viewmodel"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><category scheme="http://www.blogger.com/atom/ns#" term="メッセンジャー"/><category scheme="http://www.blogger.com/atom/ns#" term="原則"/><category scheme="http://www.blogger.com/atom/ns#" term="画面遷移"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>MVVMでメッセンジャーなしに画面遷移を実現する- 画面遷移を画面状態としてモデル化する</title><content type='html'>&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□導入&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回のブログでは、「MVVMでメッセンジャーなしに&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;画面遷移を&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;実現する - 画面遷移を画面状態としてモデル化する」&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;と題しまして、メッセンジャーなしに画面遷移を実現する方法のひとつをご紹介します。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;MVVMをご提案すると「画面遷移とダイアログ表示はどうするのか？」と、多くの技術者が聞いてきます。どうやら自分でも調べてこのあたりがMVVMの鬼門だと感じ取るようです。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;「どうやら画面遷移とダイアログ表示のためには、メッセンジャーを使うらしい。」　→　「&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;調べたけどなんだかメッセンジャーが難しい、分からん&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;。］　→　「そもそもこんなことまでして、どうしてMVVMを使わなければならないんだ？」となります。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;こうなると、画面遷移やダイアログ表示がMVVMでの問題であることを前提として、MVVMのメリット面を説明していく必要があり、このマイナスからの説明で私はいつも息切れしてしまいます。どうやら、MVVM入門者には&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;メッセンジャーは&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;ハードルが高すぎるようです&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;（そして私自身メッセンジャーをあまりよく思っていません :p ）&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;。&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;MVVMを保ちつつも、シンプルに画面遷移やダイアログ表示を実現する代替の方法が求められていると感じます。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;ダイアログ表示については、以前の私のブログで&lt;a href=&quot;http://torokonbu.blogspot.com/2011/10/mvvm.html&quot; target=&quot;_blank&quot;&gt;「MVVMでダイアログ表示のためにメッセンジャーを使用するのはおかしいのでは？」&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;と題して、実績の薄い方法ながらご説明しましたので、今回は画面遷移についてご説明します。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;実は&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;ここでご紹介する画面遷移の方法は、世の中的には&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;メジャーなはずなのですが、なぜか日本語のコンテンツを探してもなかなか見つけることができなかった方法です。なお&lt;/span&gt;、DataTemplateのDataTypeを使用しますので、Silverlightユーザはご注意ください。&lt;br /&gt;
&amp;nbsp; &lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt; &lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□サンプル画面の画面仕様 &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
サンプルとして以下のような単純な画面を考えましょう。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoDydS_7ygCOec8Al0bBFJdOBsvt1zoSrS9L7bf6ClqZZTU5Z68XmxYTuGaaDLTwmYlDEAc0Hkt0A2uTAgsnUfWwwwIgbDjhhH6mbS8xOmmx3CUrp8oO8qnvrWpb6rlklIvE581tUfruM/s1600/APage.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;268&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoDydS_7ygCOec8Al0bBFJdOBsvt1zoSrS9L7bf6ClqZZTU5Z68XmxYTuGaaDLTwmYlDEAc0Hkt0A2uTAgsnUfWwwwIgbDjhhH6mbS8xOmmx3CUrp8oO8qnvrWpb6rlklIvE581tUfruM/s400/APage.png&quot; width=&quot;400&quot; /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&amp;nbsp;Aページの画面&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTwWCo58-08wp2vFXB6G9613d47srZDx4qRks2RFNkG6xxQg0twgcdjQTZl-ij1FGXZMz3TVpwef5faVVQKG6XZaty3VE2bk0cTh4zlB5UhZr9AwjPxefWasMs1eyGnMSceDtER067Tw0/s1600/BPage.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;266&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTwWCo58-08wp2vFXB6G9613d47srZDx4qRks2RFNkG6xxQg0twgcdjQTZl-ij1FGXZMz3TVpwef5faVVQKG6XZaty3VE2bk0cTh4zlB5UhZr9AwjPxefWasMs1eyGnMSceDtER067Tw0/s400/BPage.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
Bページの画面&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
メイン画面があり、そこにはページを切り替えるトグルボタンを持っています（ちょっとわかりにくいですが、ウィンドウの上部にある「TogglePageCommand」と表示された細長いボタンがトグルボタンです）。そして、トグルボタンを押下することでＡ→ＢそしてＢ→Ａへのページ遷移を行うというものです。&lt;br /&gt;
&lt;br /&gt;
なお、Ａページにはテキストボックスを持つことにしましょう。 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□ViewModelのクラス図を作成する&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ViewModelはクラス図作ってちゃんとモデリングすることを推している私の立場もあるので、次に画面遷移をViewModelのクラス図としてモデル化してみます。&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiecj0m8wqgAJlNDSZz-AHthvWR-4rDAriWo7EQaCpdR0Df53hmLwMmabJurV0wroCw_mkoGKc_CTwbgpZRFJI2-TZVbx1HkQpN3AkvjKi_ECbyBdjscwAxpoABwyArhzPDClSXzKQCK2s/s1600/TransitionViewModel.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;br /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLlBBIzTSZVV7FKx7OlZ_mk-4LwlBZkfsEYSlRG9Jjv6ykoIlcKm5W7Xg0c9upobvrlWAbEspacyCMv5CuvtKZ_0DQB5NWCy77WpqQgCxNLtwAWluGv7T5g0LBV3lDIj1xvC8b-G9kNe0/s1600/TransitionViewModel.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;422&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLlBBIzTSZVV7FKx7OlZ_mk-4LwlBZkfsEYSlRG9Jjv6ykoIlcKm5W7Xg0c9upobvrlWAbEspacyCMv5CuvtKZ_0DQB5NWCy77WpqQgCxNLtwAWluGv7T5g0LBV3lDIj1xvC8b-G9kNe0/s640/TransitionViewModel.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
MainViewModelをメインウィンドウ用のViewModelとし、APageViewModelおよびBPageViewModelをそれぞれAページ用とBページ用のViewModelとします。&lt;br /&gt;
&lt;br /&gt;
MainViewModelが保持しているCurrentPage関連で現在のページを表現しています。これはViewModelBaseを指していますが、具体的にはそのサブクラスであるAPageViewModelインスタンスを保持している時にはAページが表示され、BPageViewModelインスタンスを保持している時にはBページが表示されることとします。&lt;br /&gt;
&lt;br /&gt;
つまり、このCurrentPage関連に保持するViewModelインスタンスを切り替えることで画面遷移を発生させることができるということです。このようにViewModelの状態を変更することで画面を制御できるのがMVVMの醍醐味ですね。&lt;br /&gt;
&lt;br /&gt;
また、MainViewModelのAPage関連とBPage関連には、それぞれＡページとBページのためのViewModelインスタンスをあらかじめ作成して保持しておくものとします。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□ViewModelの実装&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ViewModelの設計も終わったので、ViewModelの実装を以下に示します。なお、ViewModelBaseとコマンドのために、MVVM Light Toolkitを使用した実装になっていますが、その他のライブラリを使用している方は適宜読み替えてください。&lt;br /&gt;
&lt;br /&gt;
まずは、MainViewModelのコードです。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;

namespace MvvmTransition.ViewModel
{
    public class MainViewModel:ViewModelBase
    {
        public MainViewModel()
        {
            APage = new APageViewModel();
            BPage = new BPageViewModel();
            CurrentPage = APage; 
        }

        private ViewModelBase _CurrentPage;
        public ViewModelBase CurrentPage
        {
            get { return _CurrentPage; }
            set
            {
                if (_CurrentPage != value)
                {
                    _CurrentPage = value;
                    RaisePropertyChanged(&quot;CurrentPage&quot;);
                }
            }
        }

        private APageViewModel _APage;
        public APageViewModel APage
        {
            get { return _APage; }
            set
            {
                if (_APage != value)
                {
                    _APage = value;
                    RaisePropertyChanged(&quot;APage&quot;);
                }
            }
        }

        private BPageViewModel _BPage;
        public BPageViewModel BPage
        {
            get { return _BPage; }
            set
            {
                if (_BPage != value)
                {
                    _BPage = value;
                    RaisePropertyChanged(&quot;BPage&quot;);
                }
            }
        }

        private ICommand _TogglePageCommand;
        public ICommand TogglePageCommand
        {
            get { 
                if (_TogglePageCommand == null)
                {
                    _TogglePageCommand = new RelayCommand(TogglePage);
                }

                return _TogglePageCommand;
            }
        }

        private void TogglePage()
        {
            if (CurrentPage == APage) // AページだったらBページに切り替え
            {
                CurrentPage = BPage;
            }
            else if (CurrentPage == BPage) // BページだったらAページに切り替え
            {
                CurrentPage = APage;
            }
        }
    }
}
　&lt;/pre&gt;
&lt;/div&gt;
以上で、MainViewModelのコードですが、当然ながらクラス図の通りですね。なお、TogglePage（）にトグルボタンを押下した際に実行されるコードが記述されていますが、CurrentPageのViewModelのインスタンスをA用とB用で切り替えているだけです。&lt;br /&gt;
&lt;br /&gt;
次に、Ａページ用のViewModelとBページ用のViewModelです。 &lt;br /&gt;
&lt;br /&gt;
ＡページのViewModel&lt;br /&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;

namespace MvvmTransition.ViewModel
{
    public class APageViewModel:ViewModelBase
    {
        private string _AText;
        public string AText
        {
            get { return _AText; }
            set
            {
                if (_AText != value)
                {
                    _AText = value;
                    RaisePropertyChanged(&quot;AText&quot;);
                }
            }
        }
    }
}

　&lt;/pre&gt;
&lt;/div&gt;
BページのViewModel&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;pre&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GalaSoft.MvvmLight;

namespace MvvmTransition.ViewModel
{
    public class BPageViewModel:ViewModelBase
    {
    }
}

　&lt;/pre&gt;
&lt;/div&gt;
サンプルでは単純すぎて、Ａページ用のViewModelとBページ用のViewModelを説明する必要もないでしょう。実際の開発ではAページとBページは大きく異なる画面となるでしょうから、それぞれのページに適したプロパティを追加することになります。&lt;br /&gt;
&lt;br /&gt;
これでViewModelの実装は終わりました。簡単ですね。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□Viewの実装&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
次にViewの実装を説明します。まずは、単純なAページ用のViewとBページ用のViewです。&lt;br /&gt;
&lt;br /&gt;
AページのViewのコード &lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;pre&gt;&amp;lt;UserControl x:Class=&quot;MvvmTransition.APageView&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:localvm=&quot;clr-namespace:MvvmTransition.ViewModel&quot;
        xmlns:localview=&quot;clr-namespace:MvvmTransition&quot; Background=&quot;Red&quot;&amp;gt;
    &amp;lt;StackPanel&amp;gt;
        &amp;lt;Label Content=&quot;A Page&quot;/&amp;gt;
        &amp;lt;TextBox Text=&quot;{Binding AText}&quot; Width=&quot;150&quot;/&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
&amp;lt;/UserControl&amp;gt;　&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
BページのViewのコード &lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&amp;lt;UserControl x:Class=&quot;MvvmTransition.BPageView&quot;
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:localvm=&quot;clr-namespace:MvvmTransition.ViewModel&quot;
        xmlns:localview=&quot;clr-namespace:MvvmTransition&quot;  Background=&quot;Blue&quot;&amp;gt;
    &amp;lt;StackPanel&amp;gt;
        &amp;lt;Label Content=&quot;B Page&quot;/&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
&amp;lt;/UserControl&amp;gt;
　&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
これらのViewは単純なので説明も不要でしょう。&lt;br /&gt;
&lt;br /&gt;
次はメイン画面のViewのコードです。ポイントは2つあります。1つ目は、AページとBページのViewとViewModelのマッピングを実施している点、そして２つ目は、CurrentPageのViewModelを使用して現在の画面を表示を実現している点です。&lt;br /&gt;
&lt;br /&gt;
メイン画面のViewのコード &lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&amp;lt;Window
        xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
        xmlns:localvm=&quot;clr-namespace:MvvmTransition.ViewModel&quot;
        xmlns:localview=&quot;clr-namespace:MvvmTransition&quot;
        xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;&amp;nbsp;&lt;/pre&gt;
&lt;pre&gt;        xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;&lt;/pre&gt;
&lt;pre&gt;        mc:Ignorable=&quot;d&quot; x:Class=&quot;MvvmTransition.MainWindow&quot;
        Title=&quot;MainWindow&quot; Height=&quot;350&quot; Width=&quot;525&quot;&amp;gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;pre&gt;     &amp;lt;!-- AページとBページのViewとViewModelのマッピング --&amp;gt;
&lt;b style=&quot;color: red;&quot;&gt;    &amp;lt;Window.Resources&amp;gt;
        &amp;lt;DataTemplate DataType=&quot;{x:Type localvm:APageViewModel}&quot;&amp;gt;
            &amp;lt;localview:APageView /&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
        &amp;lt;DataTemplate DataType=&quot;{x:Type localvm:BPageViewModel}&quot;&amp;gt;
            &amp;lt;localview:BPageView /&amp;gt;
        &amp;lt;/DataTemplate&amp;gt;
    &amp;lt;/Window.Resources&amp;gt;&lt;/b&gt;
    
    &amp;lt;Window.DataContext&amp;gt;
        &amp;lt;localvm:MainViewModel/&amp;gt;
    &amp;lt;/Window.DataContext&amp;gt;

    &amp;lt;DockPanel LastChildFill=&quot;True&quot; &amp;gt;
        &amp;lt;Button DockPanel.Dock=&quot;Top&quot; Content=&quot;TogglePageCommand&quot; Command=&quot;{Binding TogglePageCommand}&quot;/&amp;gt;
        &amp;lt;!-- CurrentPageのViewModelを使用して現在の画面を表示 --&amp;gt;
&lt;span style=&quot;color: red;&quot;&gt;        &lt;/span&gt;&lt;b style=&quot;color: red;&quot;&gt;&amp;lt;ContentControl DockPanel.Dock=&quot;Bottom&quot; Content=&quot;{Binding CurrentPage}&quot; /&amp;gt;&lt;/b&gt;&lt;/pre&gt;
&lt;pre&gt;    &amp;lt;/DockPanel&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
「AページとBページのViewとViewModelのマッピング」では、DataTemplateの機能を利用しています。このDataTemplateを使用したViewとViewModelのマッピングは、WPFを使用したMVVMでは一般的な方法の１つです。DataTempleteの機能により、ViewModelというある種のデータを画面に表示する際に、自動的に対応するViewが適用されます。 そしてこの場合、ViewとViewModel間のDataContextの設定まで自動的に行われます。これは私がMVVMで開発している時には、最もよく使用するViewとViewModelのマッピング方法です。&lt;br /&gt;
&lt;br /&gt;
ちなみに私のViewとViewModelのマッピングの基本的なスタンスとしては、メイン画面などの頂点のウィンドウには、&lt;br /&gt;
&lt;br /&gt;
&amp;lt;Window.DataContext&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;localvm:MainViewModel /&amp;gt;&lt;br /&gt;
&amp;lt;/Window.DataContext&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
のようにXaml内でDataCotextを指定したり、コードビハインドでViewModelをViewのDataContextに設定するようにしています。そして、頂点のウィンドウ以外の場所では、（なるべく）このDataTemplateを用いたViewとViewModelのマッピングを行うようにしています。&lt;br /&gt;
&lt;br /&gt;
なお、DataTempleteでのマッピングは、システムに対して１つだけグローバルな定義があれば良いので、ResourceDictionaryの別出しのXamlファイルとして定義して、そのファイルをApp.xamlにマージすると、さらにメイン画面のViewをシンプルに保つことができます。&lt;br /&gt;
&lt;br /&gt;
次に、 「CurrentPageのViewModelを使用して現在の画面を表示」では、ContentControlのContentプロパティに、MainViewModelのCurrentPageプロパティをバインディングしています。&lt;br /&gt;
&lt;br /&gt;
ContentにViewModelの値を直接指定していますが、この場合には上記のDataTempleが適用されますので、AページのViewModelが含まれている場合にはAページのViewとなりますし、BページのViewModelが含まれている場合にはBページのViewとなります。当然、ViewModelが変更されれば内容が切り替わりますので、これによって画面遷移が実現できるというわけです。 &lt;br /&gt;
&lt;br /&gt;
ちなみに、このContentContorlを利用してViewModelをViewに変換するというのはMVVMではよく使うテクニックの一つです。また、同様にItemsControlのItemsSourceではViewModelのリストを取り扱うこともできますのでこちらも覚えておくと良いと思います。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□まとめ&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
これで、&lt;span style=&quot;font-size: small;&quot;&gt;MVVMでメッセンジャーなしに&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;画面遷移をシンプルに&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;実現する方法のご紹介は終了です。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回ご紹介した画面遷移の方法は&lt;span style=&quot;font-size: small;&quot;&gt;、&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;シンプルですし&lt;/span&gt;&lt;/span&gt;ViewModel内でしっかりと画面遷移を制御できるため個人的にお勧めです。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;また、ViewModelはこのままの構造としておいて、View側でCurrentPageを確認しながらVisibilityや&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;ZIndex&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;を変更するような方法も（ちょっとがんばる必要がありますが）ありえ&lt;span style=&quot;font-size: small;&quot;&gt;るかと思います&lt;/span&gt;。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;ちょっと話がそれますが、実はこのアプリでは、画面遷移してＡページから再びＡページへ戻ってきても、テキストの内
容が保持されたままになっています。もしも画面遷移時にデータを削除したいような場合には、トグルボタン押下時に毎度ViewModelをnewしてしま
うようにコードを修正することで実現可能です。&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;このようにViewModelはセッションのような役割を持つことができるのも特徴です。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;一方で、メッセンジャーを使用して画面遷移を行う方法はフレームワークに多く、フレームワーク側で、ViewModelのライフサイクル管理やセッション管理のようなことをやってくれるようなものも存在しますので、どのような画面遷移の方法を採用するかは、やはりプロジェクトの要件に応じて検討する必要があります。&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回は、画面遷移をシンプルに実現する方法をご説明しました。メッセンジャーによってMVVMの食わず嫌いとなることが多少なり緩和され、少しでも多くの方にMVVMの興味をもっていただければ幸いです。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
以上。&lt;br /&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/2160655483358506048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/2160655483358506048'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2011/12/mvvm.html' title='MVVMでメッセンジャーなしに画面遷移を実現する- 画面遷移を画面状態としてモデル化する'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoDydS_7ygCOec8Al0bBFJdOBsvt1zoSrS9L7bf6ClqZZTU5Z68XmxYTuGaaDLTwmYlDEAc0Hkt0A2uTAgsnUfWwwwIgbDjhhH6mbS8xOmmx3CUrp8oO8qnvrWpb6rlklIvE581tUfruM/s72-c/APage.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-9157114409310748976</id><published>2011-12-05T02:34:00.000+09:00</published><updated>2011-12-06T11:31:00.619+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mvvm"/><category scheme="http://www.blogger.com/atom/ns#" term="silverlight"/><category scheme="http://www.blogger.com/atom/ns#" term="viewmodel"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><category scheme="http://www.blogger.com/atom/ns#" term="原則"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>ViewModel設計の原則　その３</title><content type='html'>&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□導入&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回のブログでは、&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;３番目の原則として「ViewModel設計を画面設計に位置づけ積極的に設計すること」を説明します。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;順を追って説明するために、まずここでの用語の定義として、画面仕様と画面設計の言葉の使い分けについて説明します。その上で、ViewModelの画面設計について説明していきます。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
なお、ここでの説明がどのプロジェクトにも適用可能な正しい内容であるかは分からないことをあらかじめご承知おきください。ここでの内容をそれぞれのプロジェクトで使用するのはもちろん構いませんが、すべて自己責任でお願いします。&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□画面仕様と画面設計の言葉の使い分け&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
今後の説明を円滑にするため、ここでの「画面仕様」と「画面設計」という言葉を簡単ながら定義しておきます。&lt;br /&gt;
&lt;br /&gt;
ここでの「画面仕様」というのは「どのような画面を作成するのか」という画面のWhatを定義するものとします。具体的には、どのような画面と画面遷移が存在し、どのような画面レイアウトで、どのような画面の項目があり、それらの画面項目は数字なのか文字列なのか、必須なのかあるいは上限値や下限値といった
ものを定義します。&lt;br /&gt;
&lt;br /&gt;
一方、ここでの「画面設計」というのは「どのように画面を作成するのか」という画面のHowを定義するものとします。具体的には後で説明するように、ViewModelのクラス図などがこれにあたります。&lt;br /&gt;
&lt;br /&gt;
なお、他ではここでの画面仕様と画面設計という言葉の使い分けは行われていないことにご注意ください。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□みんな画面設計ってやってる？&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
「画面仕様書」や「画面定義書」や「画面設計書」という言葉は世の中でもよく使用されている言葉です。しかし、実際のところ、これらはあまり明確に使い分けら
れていないように思います（少なくとも私の周りでは）。これらは私の知る限りでは、先ほど言葉として定義した「画面仕様」を定義するためのドキュメントで
す。&lt;br /&gt;
&lt;br /&gt;
一方、先ほど言葉として定義した「画面設計」を定義するためのドキュメントは、通常は作成されません。強いて言うなら、クラス設計書などが一番近いでしょうか。しかし、これもプロジェクトの多くでは共通部分などの重要な部分に絞って作成される程度であり、またアプリケーションレベルの画面に特化して作成するとなるとさらに少なくなります。&lt;br /&gt;
&lt;br /&gt;
このように「画面設計」のためのドキュメントが作成されないてこなかったのは、まずそもそも複雑でリッチな画面を作成することが少なかったということがあります。ゲームなど一部の業界を除くと、リッチな画面が求められるようになったのは比較的最近になってのことです。しかし、モバイル系やWeb系のアプリを中心としてリッチな画面を作成する必要性は増しているので、それに応じて画面設計が必要とされるケースも増えていくでしょう。&lt;br /&gt;
&lt;br /&gt;
そして、この画面設計を目的としてMVVMパターン（より正確にはPresentation 
Modelパターン）を使用することができます。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□画面設計したいならMVVM&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
MVC
などの他の画面アーキテクチャの場合、レイヤの分けが、画面自体の抽象ではなく、画面を構成する機能の分担となっています。このため、VやCといった単位であっても個別にみてみれば実装レベルのものしかでてきません。無理に画面設計らしいものを作ろうとしても、むしろ画面を実装しながら作成したほうが良い
ようなXML Documentationと大差ないものになるのが関の山でしょう。&lt;br /&gt;
&lt;br /&gt;
しかし、MVVM（より正確に
はPresentation 
Model）のViewModelは、画面自体を抽象化した画面モデルです。また、画面仕様が存在すれば、画面の実装に先だって作成することができます。
こうしたことからViewModelのクラス図は画面設計として扱うことができるのです。&lt;br /&gt;
&lt;br /&gt;
この画面設計を行えるという利点は、ある種のプロジェクトではMVVMを採用する強い動機となり得えます。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□Eclipseプラグイン開発をしていた時の経験&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
以前私は、Eclipseのプラグイン開発を行っていましたことがあります。画面仕様もリッチで、かつ実装もそれなりに大きなものでした。画面のための共通
ライブラリを作成して、そこではドキュメント化を行ったものの、それを使用して作成される個々の画面では、画面設計らしいものは何もなくただ巨大なコード
の塊があるのみでした。&lt;br /&gt;
&lt;br /&gt;
画面の共通ライブラリによって実装をガイドとする方針でしたが、それでも画面を実装した人に
任される部分も多く、画面実装が開始されてしまうとそこから先はブラックボックスとなり、他の人は手を出すのが難しい状況となりました。開発から保守の
フェーズに移行するタイミングでソフトウェアを引継ぎきましたが、お世辞にも引き継いだといえるようなものでありませんでした。&lt;br /&gt;
&lt;br /&gt;
こうしたことはリッチな画面だから仕方がないのだと当時は思ったものですが、現在では「MVVMを使っていれば」と思うことがあります。MVVMであれば、画面のための共通フレームワークの構成も異なるものであっただろうこと、ViewModelのクラス図を使っ
てレビューもかけて品質と実装の均一化を担保できたであろうこと、画面開発のスキルの展開も容易であっただろうことを想像します。&lt;br /&gt;
&lt;br /&gt;
MVVMでの、ViewModel設計をしっかりと行うことで、ソフトウェア品質と保守性の向上に役立ちます。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;Eclipseプラグイン開発はモデリングを中心としている&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ちなみに、MVVMはWPFやSilverlightの技術ではありますが、MVVMがベースとしているはPresentationModelや
Application 
Modelと呼ばれるパターンです。私がEclipseプラグインの開発していていたころにはまだ存在しなかったのですが、実は次世代のEclipseにもこれらのパターンが導入された「Modeled UI」と呼ばれる技術が存在します。&lt;br /&gt;
&lt;br /&gt;
実はEclipseのプラグイン開発の世界は、そもそもモデリングがとても重視されている世界であり、モデル化のためのライブラリがとても充実しています。私がプラグインを開発していた当時はMVVMのような汎用的な画面自体のモデリングは（たぶん）なかったものの、UMLツールのようなグラフィカルなツールを作成するライブラリや、その他多くのライブラリがモデルをプラグインして拡張されることを当たり前としていました。そして、Modeled UIと呼ばれる技術も、比較的モデリング色が強い技術となっています。Eclipseプラグイン機構自体が巨大なDSLとメタモデルをプラグインしていくための基盤のようなものであり、そしてＵＩ自体も複雑になりがちなので、当然のようにモデリングが中心となっているのです。&lt;br /&gt;
&lt;br /&gt;
このときの私の経験はMVVMでの開発にも生かされました。&lt;br /&gt;
&lt;br /&gt;
WPF/Silverlightのデータバインドやその他の技術は、EclipseやJavaが持つ技術に比べるととても強力です。使いこなせばMVVMを強力にサポートしてくれます。しかし私が初めてMVVMを勉強したとき、足元をすくわれたのもこれらの技術でした。たくさんの技術にばかり目を奪われて方向性を見失い、結局のところどうしてよいのか分からず途方に暮れてしまったのです。&lt;br /&gt;
&amp;nbsp; &lt;br /&gt;
この時に、Eclipseプラグインでのモデリングの経験が役に立ちました。ViewModel（Presentation Model)をしっかりとモデル化する道をとることで、これら技術をどこで使うべきなのかをガイドしてくれるようになったのです。&lt;br /&gt;
&lt;br /&gt;
そして、ViewModelのモデリングは、Eclipseのような手厚いモデルのためのサポートがなくてもあまり問題にならないこともこのとき分かりました。（ただし、何かViewModelを生成するような仕組みがあったほうが便利ではあります。自分のプロジェクトでもViewModelの自動生成の機構は開発しています。）&lt;br /&gt;
&lt;br /&gt;
ViewModelのモデリングなしでは今でもMVVMで途方に暮れていたことは容易に想像できます。モデリングによるUIの開発はEclipseでの専売特許ではありません。WPF/Silverlightはモデリングのための技術が薄いですが、それでも全体的にはMVVMのための手厚い技術が存在しており、むしろEclipseよりも適しています。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[原則３]ViewModel設計を画面設計に位置づけ積極的に設計すること&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
MVVMは画面設計を行うことができるUIアーキテクチャです。プロジェクトの性質によっては、採用の大きな動機となり得ます。ViewModel設計をしっかりと行うことで、ソフトウェア品質と保守性の向上に役立ちます。&lt;br /&gt;
&lt;br /&gt;
ViewModelをしっかりとモデリングすることで、MVVMの技術に振り回されず、これらを支配下においてください。MVVMの本質は画面のモデリングであり、そしてViewModelの設計であるといっても過言ではありません。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;[原則３]&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;ViewModel設計を画面設計に位置づけ積極的に設計すること&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
これで、今回のブログは終了です。長文お疲れ様でした。&lt;br /&gt;
これまでの説明ではViewModel設計の位置づけ的なお話が多かったと思いますが、次回以降では、よりViewModelの設計方法について説明していきます。技術者にとっては次回以降の方が読んで楽しいかもしれませんね。ご期待ください。 &lt;br /&gt;
&lt;br /&gt;
以上。&lt;br /&gt;
&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/9157114409310748976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/9157114409310748976'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2011/11/viewmodel_30.html' title='ViewModel設計の原則　その３'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-5495674754098959040</id><published>2011-11-22T23:44:00.010+09:00</published><updated>2011-12-01T11:34:26.985+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mvvm"/><category scheme="http://www.blogger.com/atom/ns#" term="silverlight"/><category scheme="http://www.blogger.com/atom/ns#" term="viewmodel"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><category scheme="http://www.blogger.com/atom/ns#" term="原則"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>ViewModel設計の原則　その２</title><content type='html'>&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□導入&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;今回のブログでは、&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;2番目の原則として「画面定義後、Viewの実装の前にViewModelの（簡易）設計を行うこと」を説明します。順を追って説明するために、まずViewModel設計の優れた例としてMSDN Manazineのサンプルを見てみます。これをベースとして、「画面定義後にViewModelの（簡易）設計を行う必要があること」を説明します。その後、「&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;ViewModelの設計を&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;行った後に&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;Viewの実装を&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;行う必要があること」を説明します。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
なお、ここでの説明がどのプロジェクトにも適用可能な正しい内容であるかは分からないことをあらかじめご承知おきください。ここでの内容をそれぞれのプロジェクトで使用するのはもちろん構いませんが、すべて自己責任でお願いします。&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□ViewModel設計の例&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
それでは早速、良いMVVMの設計例として、MSDN Manazineのサンプルを見てみましょう。ここで紹介されているのは、顧客管理のためのサンプルであり、以下のような画面です。&lt;br /&gt;
&lt;br /&gt;
「Model-View-ViewModel デザイン パターンによる WPF アプリケーション」&lt;br /&gt;
&lt;a href=&quot;http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx&quot;&gt;http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx&lt;/a&gt;&lt;br /&gt;
（このMSDN Magazineのサンプルコードは、MVVMを学ぶ上でとても参考になりますので、MVVMを初めて学ぶ際には是非ソースコードまでご覧ください。） &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5EGzE7195IjySMjenpPlopJ43wh0G3HXYJcLgoy5DKqz2pQesdLELIZHAXdGDRbO9WNHz-K6W_QZ3Gpx-anTU61qsTk8qfqNlft0zgPPMZksDutXqeu2qPRKmwCKr9iP7RdmIYvr6UZU/s1600/ViewAllCustomers.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;260&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5EGzE7195IjySMjenpPlopJ43wh0G3HXYJcLgoy5DKqz2pQesdLELIZHAXdGDRbO9WNHz-K6W_QZ3Gpx-anTU61qsTk8qfqNlft0zgPPMZksDutXqeu2qPRKmwCKr9iP7RdmIYvr6UZU/s400/ViewAllCustomers.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
顧客リストをワークスペースに表示&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyR5yPec5JmQ0Ck6UiQulV0wyL52Noa2zc3kxEDcSKv_4smdxRV17wmTnp9UfWBl7mDy8ZTHxTiSuudKqmPhGi8-MyczGctgXJOdzEapYnGX77Q9W9XsewJJhyXki3QTs5XqFCm_7m_Rw/s1600/CreateNewCustomer.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;257&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyR5yPec5JmQ0Ck6UiQulV0wyL52Noa2zc3kxEDcSKv_4smdxRV17wmTnp9UfWBl7mDy8ZTHxTiSuudKqmPhGi8-MyczGctgXJOdzEapYnGX77Q9W9XsewJJhyXki3QTs5XqFCm_7m_Rw/s400/CreateNewCustomer.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
新規顧客作成用画面をワークスペースに表示&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
メイン画面の中が左右2つに分かれています。右側には顧客情報の参照や編集のためのワークスペースが存在します。これはVisual Studioのエディタ領域をイメージすると分かりやすいかと思います。一方、左側には顧客リストを表示するためのリンクと、新規顧客作成用画面を表示するためのリンクが存在しています。これらのリンクを押下することでワークスペースに顧客リスト、あるいは新規顧客作成画面がタブとして表示されます。&lt;br /&gt;
&lt;br /&gt;
この画面のViewModelの構造をクラス図で見てみます。（MSDN Manazineにもクラス図はありますが、ViewModelの全体の構造が分かりにくいため、サンプルのソースコードから改めて私の方でクラス図を書き起こしました。）&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiij9UlK7o18gJapRB7eFkH2JAPNXVW7V3yGhE5AjDyNmG8jtoggdeGsL5n_gtaQBQf29ZgMg2077Iv7zhDFeoVJgIoeTBFe1FtR5x4s8IrPGb7AwYTmVQcAmMEBdd35rNNb2ntaitaTw8/s1600/MsdnSampleClassDialgram.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;404&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiij9UlK7o18gJapRB7eFkH2JAPNXVW7V3yGhE5AjDyNmG8jtoggdeGsL5n_gtaQBQf29ZgMg2077Iv7zhDFeoVJgIoeTBFe1FtR5x4s8IrPGb7AwYTmVQcAmMEBdd35rNNb2ntaitaTw8/s640/MsdnSampleClassDialgram.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
ViewModelBaseがすべてのViewModelの共通親クラスとして存在しています。&lt;br /&gt;
&lt;br /&gt;
WorkspaceViewModelはCloseCommandプロパティを持ち、クローズ可能なViewModelとしての意味合いを持っています。それらのサブクラスとして、新規顧客作成のためのCustomerViewModelと、顧客リストのためのAllCustomerViewModelと、メイン画面のためのMainWindowViewModelがあります。&lt;br /&gt;
（なお、個人的にはWorkspaceViewModelのサブクラスは、ワークスペースのタブに含まれるものだけにして、MainWindowViewModelを含めないほうが良かったのでは、と思います）&lt;br /&gt;
&lt;br /&gt;
メイン画面を表すMainWindowViewModelを見てみると、Workspaces関連に自身がタブとして持つWorkspaceViewModelのリストを保持しています。また、Commands関連に顧客リストを表示するためのリンクと、新規顧客作成用画面を表示するためのリンクを表現するCommnadViewModelをリストとして保持しています。&lt;br /&gt;
&lt;br /&gt;
このように継承関係やコンポジション関連により、ViewModel全体がしっかりとした構造を持ち、連携が行われていることが読み取れます。&lt;br /&gt;
&lt;br /&gt;
これは比較的単純な画面の例ではありますが、複雑な画面になれば、構造もそれに応じて大きくなっていきます。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□画面構造にViewModel構造は依存する&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
このサンプルプログラムで確認しておきたいのが、画面構造とViewModel構造の対応関係です。メイン画面の左側のリンク群と右側のワークスペースのタブ群の構成に対応して、MainWindowViewModelにリンク群を表現するCommands関連と、ワークスペース群を表現するWorkspaces関連が存在します。&lt;br /&gt;
&lt;br /&gt;
このように画面構造（画面の概念的な構造）とViewModelの構造とが対応づいているのは偶然ではありません。これは明に暗に画面のモデリングの結果としてViewModelが定義されているからです。そしてViewModel設計のインプットとして画面仕様が必要であり、またViewModel構造は画面仕様（画面構造）に依存するということでもあります。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□ViewModel設計のための画面仕様は、まずはホワイトボードレベルで構わない&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ViewModel設計に先だって画面仕様が決まっていなければならないことを説明しました。&lt;br /&gt;
&lt;br /&gt;
このViewModel設計のための画面仕様は、まずはホワイトボードに記述する大雑把なレベルのもので構いません（もちろん詳細に決まっていて困るものでもありませんが）。これは、ViewModelは段階的に詳細化していけば良いためです。&lt;br /&gt;
&lt;br /&gt;
ドメインモデルの設計でも、細かな振る舞いはまずはおいておいて、大雑把な構造を洗い出すことを優先することが多いのではないでしょうか。同様にViewModelの設計でも、まずは大雑把な構造を洗い出すことを優先したほうがうまくいきやすいように思います。実際のところ、よほど簡単な画面でもない限りViewModel設計をはじめから完璧に仕上げるのは不可能です。&lt;br /&gt;
&lt;br /&gt;
特にViewModelでは、画面のための細かな属性や操作が多くなり、またドメインモデルよりも実装寄りの設計となる傾向があります。このため、ViewModel設計で細部に注目してしまうと全体像がおろそかになって袋小路に入り込みやすいのです。属性や操作の細かな
洗い出しはまずはおいていて、箱と線による構造の洗い出しに注力するようにしてください。細かな属性や操作はViewModelを実装しながら洗い出して行っても良いと思います。&lt;br /&gt;
&lt;br /&gt;
画面仕様もViewModelもそれぞれ段階的に肉付けしていけば良いのです。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□[余談]WPFからSilverlightへの移行は容易か&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ところで話が逸れますが、MVVMで開発した場合、WPFからSilverlightへの移行のように、UIシステムの変更が可能だといわれていま
す。これは、WPFもSilverlightもViewに属するので、UIシステムの変更でViewは変わっても、ViewModelはそのまま再利用可
能であるということを指しており、実際その通りではあるのですが注意が必要です。というのもViewModelの構造は画面仕様に依存します。このため、
ViewModelを変更せずに済むのは、画面仕様を変更しない（あるいはほとんど変更しない）で移行した場合の話だということです。&lt;br /&gt;
&lt;br /&gt;
も
しも、WPFからSilverlightへの移行が、PC画面での使用からモバイルでの使用を意図したものであるとすると、画面サイズの制約により画面仕
様が根底から異なるものになるでしょう。こうした場合には、ViewModelを大きく修正する必要があり、流用可能なViewModelのコードはおそ
らく半分もありません。むしろPC画面用とモバイル用のViewModelの無理な共通化は、ViewModelを複雑怪奇な構成にしてしまう可能性があ
ります。そういったことをするよりはViewModelを最小限の共通化にとどめてイチから別途開発ししつつ、共通化はModelのレイヤで目指したほう
が良いことも多いと思います。&lt;br /&gt;
&lt;br /&gt;
個人的には、MVVMによるUIシステムの変更容易性のメリットは、少し過大評価されているように思います。&lt;br /&gt;
&lt;br /&gt;
私は以前、ViewModelによってWPFからSilverlightなどへのUIシステムの変更に強いことから、「ViewModelは画面仕様の変更にも強くなければならず、画面仕様から独立した存在としなければならないのではないか」と勘違いしていたことがあります。しかし、このような
勘違いは、ViewModelの存在を、どのような画面にでも使用できるような、単なるModelラッパーにまで責務の範囲を狭め、有効な
ViewModelを設計できない原因となります。変な表現ではありますが「むしろ安心してViewModelを画面仕様に依存させてください」。&lt;br /&gt;
&lt;br /&gt;
なお、ViewModelは画面仕様に依存するのですが、ViewModelがあることで画面の変更容易性が下がるという意味ではないことに注意してくださ
い。実際のところ、ViewModelは画面のモデルであり、抽象化が行われる分だけ画面の修正には強くなります。しかしそうはいっても、画面構造の大き
な変化には、ViewModelであっても大きな影響を受けるということです。&lt;br /&gt;
&lt;br /&gt;
（なお、ViewModelにも画面
仕様よりのViewModelと、モデルよりのViewModelがあり、このため画面仕様の変更のおよぶViewModelと、変更の及ばない
ViewModelがあります。この辺りについては、機会があればご説明したいと思います。）&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□ViewModelを検討してから、Viewを実装するという流れを守る&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
前回のブログの「期限が迫っている項目を赤く強調表示したい」というストーリーにて、ViewModelによる抽象化された画面に対するプログラミングの実現と、ViewModelとViewの主従関係について説明しました。このように画面の制御はViewModelに対して行われ、Viewがそれを実現するという関係があります。&lt;br /&gt;
&lt;br /&gt;
コンポーネントに例えるとViewModelがインタフェースであり、Viewはその実装といっても良いでしょう（※1）。こうして考えると、手順としてインタフェースであるViewModelを検討してから、実装であるViewを検討するというのはごく自然な流れです。この検討の流れは必ず守るようにしてください。&lt;br /&gt;
&lt;br /&gt;
この流れを守ることでViewが実現すべき目標をViewModelが与えてくれるようになります。Viewは、ViewModelが意図する仕様に向けて実装すれば良いのです。明確な目標が存在することで高い生産性を保ちつつViewのプログラミングをすることが可能です。&lt;br /&gt;
&lt;br /&gt;
一方、View側から着手してViewModelを作成していくスタイルの開発を進めていくと、ViewとViewModelの境界を探り探りプログラミングすることとなり、自信を持ってプログラミングすることができません。作成されるViewModelも、他のViewModelと連携が行われない孤島のようなViewModelとなりやすく、結果としてViewModelとしての力を発揮することができません。 こうなってしまうとViewModelを導入せずににコードビハインドで実現したほうが、むしろ手数が少なく幾分かマシという状況にさえなってしまいます。&lt;br /&gt;
&lt;br /&gt;
こうしたView側から着手してしまう状況は、幸か不幸かVisual StudioやExpression  Blendで画面仕様の定義を兼ねて簡単にXamlでスケッチをかけことにより、比較的容易に発生します。スケッチとして作成したXamlをそのままViewへと転用することで、ViewModelの設計がなおざりになってしまうのです。こうしたスケッチは、そのままMVVMのViewとして使用するのではなく、  ViewModelの設計を行ったうえで、使用できる部分だけ利用する程度に留めることをお勧めします。基本的にはそうしたスケッチは  すべて捨てるぐらいの覚悟で丁度良いと思います。 &lt;br /&gt;
&lt;br /&gt;
ViewModelを検討してから、Viewを実装するという流れを守るようにしてください。&lt;br /&gt;
&lt;br /&gt;
※１：一般的なインタフェースと違って、ViewModelは単体として意味を持つものではありません。複数のViewModelインスタンスが、 オブジェクトツリー（あるいはグラフ）となり、その構成全体として画面の状態を表現します。こうした点は異なるものの、ViewModelとViewには 主従関係があり、イメージとしてViewModelがインタフェース、Viewはその実装と考えても問題ないと思います。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□&lt;/b&gt;&lt;b&gt;[原則２]&lt;/b&gt;&lt;b&gt;画面定義後、Viewの実装の前にViewModelの（簡易）設計を行うこと&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
今ままでの説明が、画面仕様の定義後にViewModel設計を行うこと、またViewModel設計の後にViewの実装を行うべきであるということの解説となります。&lt;br /&gt;
&lt;br /&gt;
Viewを実現するWPF/Silverlightや、MVVMフレームワークなど、これら自体が大きな技術ですので、圧倒されてしまって結果的にViewを開発の中心&lt;span style=&quot;font-size: small;&quot;&gt;に据えてしまう気持ちも分からないではありません。しかし、これらはあくまで実現手段にすぎません。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;そうではなくViewModelを中心に据えるようにしてください、、、と、なんと実はこちらも間違いです。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;「本当の意味で中心に据えるべきはユーザであり、そこから段階的に詳細化しながら導かれていくのが、システム仕様であり、機能仕様であり、画面仕様であり、画面設計であり、ViewModelであり、Viewの実装なのです」。むしろこちらの方がソフトウェア開発全体を捉えたより正しい原則といえるかもしれません。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
ViewModelさえもあくまで画面の実現手段にすぎません。私自身よく忘れてしまうことですが、技術に振り回されずに、より大きな目的の中に技術を位置づけることが大切です。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;[原則２]&lt;/span&gt;&lt;/b&gt;&lt;b&gt;画面定義後、Viewの実装の前にViewModelの（簡易）設計を行うこと&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
これで、今回のブログは終了です。長文お疲れ様でした。&lt;br /&gt;
&lt;br /&gt;
以上。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/5495674754098959040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/5495674754098959040'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2011/11/viewmodel.html' title='ViewModel設計の原則　その２'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5EGzE7195IjySMjenpPlopJ43wh0G3HXYJcLgoy5DKqz2pQesdLELIZHAXdGDRbO9WNHz-K6W_QZ3Gpx-anTU61qsTk8qfqNlft0zgPPMZksDutXqeu2qPRKmwCKr9iP7RdmIYvr6UZU/s72-c/ViewAllCustomers.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-4752649174951168492</id><published>2011-10-27T19:46:00.017+09:00</published><updated>2012-03-08T12:14:16.108+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mvvm"/><category scheme="http://www.blogger.com/atom/ns#" term="silverlight"/><category scheme="http://www.blogger.com/atom/ns#" term="viewmodel"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><category scheme="http://www.blogger.com/atom/ns#" term="原則"/><category scheme="http://www.blogger.com/atom/ns#" term="設計"/><title type='text'>ViewModel設計の原則　その１</title><content type='html'>&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□導入&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
MVVMの知名度は十分に高まっているものの、MVVMでの開発はまだまだしきいが高いと感じる方が多いのではないかと思います。確かに、MVVMで開発するためのライブラリやフレームワークが出揃い始めており、それらの技術について説明したコンテンツも充実しつつあるように思います。しかし、これらの技術をどう使って設計していけばよいのかはどのコンテンツにもあまり語られていません。特にMVVMの肝であるはずのView Modelの設計をどうしたらよいのかについて説明しているコンテンツがなく、各技術者に任されてしまっています。&lt;br /&gt;
&lt;br /&gt;
私自身、模索を繰り返しながら何とかViewModelの設計をやっているのが実態で、まだまだ不明な点が多くあります。しかし、今まで私がMVVMによる開発を行ってきたなかで得たものを、「View Model設計のための原則」というテーマで、何回かに分けてご紹介したいと思います。いくらかでもMVVMによる開発の参考になれば幸いです。&lt;br /&gt;
&lt;br /&gt;
なお、ここでの説明がどのプロジェクトにも適用可能な正しい内容であるかは分からないことをあらかじめご承知おきください。ここでの内容をそれぞれのプロジェクトで使用するのはもちろん構いませんが、すべて自己責任でお願いします。&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□MVVMパターンとは&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
早速ですが、そもそもMVVMパターンとは何かというと「Model-View-ViewModelパターン」のことです。&lt;br /&gt;
&lt;br /&gt;
MVVMでのModelとは、ビジネスロジックとデータを表現し、一般にドメインモデルと呼ばれたりする&quot;モデル&quot;です。データベースを使用した場合にはORマッピングされるエンティティや、サービスとして実装される部分です。MVVMパターンはGUIのためのパターンであるため、本ブログではあまり説明しませんが、このモデル自体、システムの中でとても大きな位置を占めるので注意が必要です。&lt;br /&gt;
&lt;br /&gt;
MVVMでのViewは、画面コントロール、画面レイアウト、フォント、色彩、アニメーション、ＵＩロジックを表現します。通常はXAMLで実装し、可能な限りコードビハインドは使用しません。これは、XAMLの方が宣言的に画面を定義できるからですが、決してコードビハインドを使用してはならないという訳ではないことに注意してください。&lt;br /&gt;
&lt;br /&gt;
MVVMの最後の要素であるViewModelは、プレゼンテーションロジックを表現し、画面に表示・編集する値をプロパティとして保持し、またボタン押下時などに実行すべき処理をコマンドと して保持します。そして、ViewModelの最も重要な側面として「Model of the  View(ビューのモデル)」を表現するというものがあります。つまりViewModelとは画面そのものを抽象化したもの、さらに言い換えると画面そのものをモデル化したものということです。&lt;br /&gt;
&lt;br /&gt;
ViewとViewModelの状態は、常に同期が行われるのがMVVMでの大きな特徴です。この同期は主に データバインドとデータトリガ―の機能により実現します。（ただし、高度な同期についてはビヘイビアを自作する必要に迫られることもあります。）ViewModelの状態変化はViewの状態変化をもたらし、また逆にViewの状態変化はViewModelの状態変化をもたらします。ただし考え方としては、ViewとViewModelは対等の関係ではなく、ViewModelが主で、Viewが従の、主従関係にあると考えた方がよいでしょう。&lt;br /&gt;
&lt;br /&gt;
Viewには画面コントロール、画面レイアウト、フォント、色彩、アニメーションなどの画面のあらゆる要素が存在しています。しかし、これらを細かくコントロールしたくないし、抽象的かつ容易に扱いたいのです。そこにViewModelの価値があります。実は、同期の仕組みにより、画面を抽象化したViewModelに対してプログラミングを行うことで、間接的にViewをコントロールできます。つまり、高い抽象を保ったまま画面をコントロールできるということです。これこそがMVVMのもっとも大きなメリットなのです。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;□[原則１]ViewModelをモデリングの対象として認識すること&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Viewでは、画面コントロール、画面レイアウト、フォント、色彩を司り、ViewModelでは画面に表示する値やコマンドを司ると前述しました。しかし、「ViewModelにてフォントや色彩を保持してはいけないのか？」というと実はそんなことはないのです。&lt;br /&gt;
&lt;br /&gt;
実は本来のViewModelとは、「あなたが画面において制御したいと考えたもの」です。これはつまり、あなたが画面において何が重要であり、また何を無視すべきなのかを判断したモデリングの結果がViewModelであるということです。&lt;br /&gt;
&lt;br /&gt;
そういった意味において、フォントや色彩が重要であると考えた場合には、ViewModelにこれらが含まれることは全く問題ありません。しかし、確かに多くの場合にフォントや色彩などをViewModelは保持しません。これはどういったことなのでしょうか？例を挙げて説明しましょう。&lt;br /&gt;
&lt;br /&gt;
「期限が迫っている項目を赤く強調表示したい」というストーリーを考えてみましょう。この場合「期限が迫っている項目として強調表示すべきか」ということが画面の意図として重要となります。このため、ViewModelでは強調表示するかどうかのBooleanのフラグを保持することになり、この抽象化されたフラグに対してプログラミングを行い、画面をコントロールします。&lt;br /&gt;
&lt;br /&gt;
一方、強調表示するために、赤くするのか、フォントを変えるのか、アニメーションを行うのか、あるいはその組み合わせを実行するのかは画面の抽象からすると重要ではありません。このためこれはView側の役割となり、データトリガ―機能などでこのViewModelのフラグを監視して、赤くするといったことを実現します。&lt;br /&gt;
&lt;br /&gt;
ViewModelによる抽象化された画面に対するプログラミングの実現と、ViewModelとViewの主従関係とはこういったことを指しています。そして、フォントや色彩がViewModelにて保持されることがあまりないのは、画面の意図のレベルにおいて重要と判断されることが少ないからということになります。&lt;br /&gt;
&lt;br /&gt;
画面そのものをモデル化したものがViewModelであるという考え方は、いくら強調しても強調しすぎることはありません。これは、ビジネスをモデル化してドメインモデルを作成するように、画面をモデル化してViewModelを作成する必要があるということです。漫然と開発してもドメインモデルが 生まれないのと同様に、積極的にViewModelをモデリングしないとViewModelが生まれることもありません。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;[原則１]ViewModelをモデリングの対象として認識すること&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;b&gt;□ViewModel設計の原則&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
ここまで、ViewModel設計のl1つ目の原則として「ViewModelをモデリングの対象として認識すること」を説明してきました。今回ブログの最後に唐突ではありますが、本シリーズのテーマとなっているViewModel設計の原則を以下に列挙します。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;・[原則１]ViewModelをモデリングの対象として認識すること&lt;/b&gt;&lt;br /&gt;
&lt;b&gt; ・&lt;/b&gt;&lt;b&gt;[原則２]&lt;/b&gt;&lt;b&gt;画面定義後、Viewの実装の前にViewModelの（簡易）設計を行うこと&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;・&lt;/b&gt;&lt;b&gt;[原則３]&lt;/b&gt;&lt;b&gt;ViewModel設計を画面設計に位置づけ積極的に設計すること&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;・&lt;/b&gt;&lt;b&gt;[原則４]&lt;/b&gt;&lt;b&gt;あらゆる画面の状態をViewModelでの状態としてモデル化すること&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;・&lt;/b&gt;&lt;b&gt;[原則５]&lt;/b&gt;&lt;b&gt;イベントではなく、ViewModelの状態によって画面を制御すること&lt;br /&gt;
&lt;/b&gt;&lt;b&gt;
&lt;/b&gt;&lt;b&gt;・&lt;/b&gt;&lt;b&gt;[原則６]&lt;/b&gt;&lt;b&gt;いつでもViewを再構成できるだけの情報をViewModelに保持させること&lt;br /&gt;
・&lt;/b&gt;&lt;b&gt;[原則７]&lt;/b&gt;&lt;b&gt;ViewとViewModelとは平行なツリー関係を維持すること &lt;br /&gt;
・&lt;/b&gt;&lt;b&gt;[原則８]&lt;/b&gt;&lt;b&gt;ViewModelツリーの親子関係は慎重に設計し、親は子に依存しても、子は親に依存させないこと&lt;/b&gt;&lt;br /&gt;
&lt;b&gt; &lt;/b&gt;&lt;br /&gt;
&lt;b&gt;（※しっかりとまとめきれていないので、今後変更される可能性があります）&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
これらの原則の詳細は次回以降に説明します。&lt;br /&gt;
&lt;br /&gt;
以上。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/4752649174951168492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/4752649174951168492'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2011/10/viewmodel.html' title='ViewModel設計の原則　その１'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-24166670934445279.post-5798670310793127583</id><published>2011-10-11T18:28:00.008+09:00</published><updated>2012-01-05T15:46:59.920+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="dialog"/><category scheme="http://www.blogger.com/atom/ns#" term="messenger"/><category scheme="http://www.blogger.com/atom/ns#" term="mvvm"/><category scheme="http://www.blogger.com/atom/ns#" term="presentation model"/><category scheme="http://www.blogger.com/atom/ns#" term="silverlight"/><category scheme="http://www.blogger.com/atom/ns#" term="viewmodel"/><category scheme="http://www.blogger.com/atom/ns#" term="wpf"/><title type='text'>「MVVMでダイアログ表示のためにメッセンジャーを使用するのはおかしいのでは？」 -ダイアログ表示を画面状態としてモデル化する-</title><content type='html'>&lt;br /&gt;
&lt;div style=&quot;color: red;&quot;&gt;
[2012/01/05 補足]&lt;b&gt;&lt;span style=&quot;color: black;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;color: black;&quot;&gt;本記事を記述した後、ダイアログ表示のためにライブラリを作成しました。このライブラリの紹介と解説のために「&lt;/span&gt;&lt;a href=&quot;http://torokonbu.blogspot.com/2011/12/mvvmmvvm-dialog-behavior.html&quot; style=&quot;color: blue;&quot; target=&quot;_blank&quot;&gt;MVVMでメッセンジャーを使わずにダイアログを表示する(MVVM Dialog Behaviorライブラリ提供)&lt;/a&gt;&lt;span style=&quot;color: black;&quot;&gt;」と題したブログを改めて記述しましたので、必要に応じてこちらをご参照ください。&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div style=&quot;color: black;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #666666;&quot;&gt;
MVVMでダイアログ表示をするためにメッセンジャーを使うのは、現在では最も主流の方法でしょう。しかし以前、MVVMの説明を行ってすぐの技術者に「MVVMでダイアログ表示のためにメッセンジャーを使用するのはおかしいのでは？」と聞かれたとき、説明を続けられずに途中で窮してしまったことがあります。 &lt;/div&gt;
&lt;div style=&quot;color: #666666;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #666666;&quot;&gt;
MVVMパターンはPresentation Modelパターンの一種であり、ViewModelに抽象化された画面状態を保持します。一方、メッセンジャーを使用する方法は、処理の起動であって、あくまでイベントを送るようなものであり、画面状態とは相容れない考え方です。&lt;/div&gt;
&lt;br /&gt;
イメージボタンのようなボタンの中のコンテンツや、MetroUIのようなタイルの中のコンテンツが画面状態と認められる一方で、どうしてダイアログ表示は画面の状態とは認められないのでしょうか？疑問に思い始め、今ではダイアログ表示は画面の状態であるという 考え方を支持しています。&lt;br /&gt;
&lt;br /&gt;
それでは、メッセジャーを使わずにダイアログ表示を実現するにはどうすればよいのでしょうか。最近になって考えている方法をご紹介します。なお残念ながら、この方法は実プロジェクトで実績を積んでいません。あくまで考え方とサンプルとしてお楽しみください。&lt;br /&gt;
&lt;br /&gt;
メッセンジャーを使わずに、ダイアログを画面状態として扱うためには、改めてPresentation Modelパターンに立ち戻る必要があります。&lt;br /&gt;
&lt;br /&gt;
Presentation Modelパターンは、画面の抽象的な状態を表現し、その抽象レベルのまま画面を管理するための方法です。ViewではなくPresentation Modelが主体となるため、スタートとしてダイアログ表示を抽象的にとらえるとどのようなPresentaion Modelとなるのかをまず考える必要があります。&lt;br /&gt;
&lt;br /&gt;
MVVMパターンでは、Presentation ModelはView Modelのことですので、つまるところ、ダイアログ表示をViewModelとして設計するとどのようになるのかということから考える必要があるということです。&lt;br /&gt;
&lt;br /&gt;
ダイアログ表示を 比較的単純にとらえた場合、モデルとしては以下のようになります。&lt;br /&gt;
&lt;br /&gt;
MainViewModel&amp;nbsp; ◆------ 0,1&amp;nbsp; DialogViewModel&lt;br /&gt;
&lt;br /&gt;
MainViewModelがダイアログ呼び出し元のViewModelとして存在します。&lt;br /&gt;
&lt;br /&gt;
そして、ダイアログが表示されているかどうかは、MainViewModelが持つDialogViewModel型の属性に、インスタンスが存在するかNullかで判断するというモデルです。これは、上記の「0,1」という多重度に現れています。つまりダイアログ表示を行うためには、MainViewModelが持つDialogViewModel型の属性にDialogViewModelのインスタンスをセットし、ダイアログクローズのためにはNullにセットしなおせばよいことになります。&lt;br /&gt;
&lt;br /&gt;
まずは、MainViewModelのコード例です。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;public class MainViewModel
    {
        private DialogViewModel _DialogViewModel;

        public DialogViewModel DialogViewModel
        {
            get
            {
                return _DialogViewModel;
            }
            set
            {
                if (_DialogViewModel != value)
                {
                    _DialogViewModel = value;
                    OnPropertyChanged(&quot;DialogViewModel&quot;);
                }
            }
        }

        private InvokeCommand _OpenDialogCommand;
        public InvokeCommand OpenDialogCommand
        {
            get
            {
                if (_OpenDialogCommand == null)
                {
                    _OpenDialogCommand = new InvokeCommand(OpenDialog);
                }
                return _OpenDialogCommand;
            }
        }

        public void OpenDialog()
        {
            // ダイアログオープン
            DialogViewModel = new DialogViewModel(CloseDialogAction) { UserName = &quot;ユーザ名だよ&quot;, Password = &quot;パスワードだよ&quot; };
        }

        public void CloseDialogAction()
        {
            // ダイアログクローズ
            DialogViewModel = null;
        }
    }

&lt;/pre&gt;
&lt;/div&gt;
まず注目すべきは、ダイアログオープンのために、 DialogViewMode属性へのインスタンスのセットしか行っていない点です。そして、ダイアログに引き渡すべき、ユーザ名やパスワードがViewModelのプロパティによる単純な受け渡しで実現できており、メッセンジャーのように値の引き渡しや値の返り値を意識したコーディングではなくなっています。このように、ダイアログでの間の連携がしやすいのは、ダイアログを画面状態とした際のメリットです。&lt;br /&gt;
&lt;br /&gt;
また、CloseDialogAction()メソッドでは、ダイアログクローズのためには、DialogViewMode属性にNullを設定しています。なお、ここで問題になるのが、どこからこのCloseDialogAction（）メソッドを呼び出すのかということです。ここでは、ダイアログ側からのクローズを考慮して、このメソッド自体をDialogViewModelのコンストラクタに渡して、呼び出せるようにしていることに注意してください。このあたりの実装方法は、いろいろ考えられそうですので、それぞれのプロジェクトに合わせて実装すればよいと思います。&lt;br /&gt;
&lt;br /&gt;
次に、DialogViewModelのコード例です。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;public class DialogViewModel
    {
        public Action CloseAction;

        public DialogViewModel(Action closeAction)
        {
            CloseAction = closeAction;
        }

        private String _UserName;
        public String UserName
        {
            get
            {
                return _UserName;
            }
            set
            {
                if (_UserName != value)
                {
                    _UserName = value;
                    OnPropertyChanged(&quot;UserName&quot;);
                }
            }
        }

        private String _Password;
        public String Password
        {
            get
            {
                return _Password;
            }
            set
            {
                if (_Password != value)
                {
                    _Password = value;
                    OnPropertyChanged(&quot;Password&quot;);
                }
            }
        }

        private InvokeCommand _LoginCommand;
        public InvokeCommand LoginCommand
        {
            get
            {
                if (_LoginCommand == null)
                {
                    _LoginCommand = new InvokeCommand(Login);
                }
                return _LoginCommand;
            }
        }

        private InvokeCommand _CancelCommand;
        public InvokeCommand CancelCommand
        {
            get
            {
                if (_CancelCommand == null)
                {
                    _CancelCommand = new InvokeCommand(Cancel);
                }
                return _CancelCommand;
            }
        }

        public void Login()
        {
            if (DoLogin())
            {
                CloseAction();
            }
        }

        public void Cancel()
        {
            CloseAction();
        }

        public bool DoLogin()
        {
            // 本来はログイン処理を記述する
            return true;
        }
　&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
ログインダイアログをイメージして、ユーザ名とパスワードの属性を用意しています。大したコードでもありませんが、先ほど説明したとおり、自身のダイアロ グをクローズするために、MainViewModelから渡されたCloseDialogAction()メソッドをコールバックしている点はご注意くだ さい。&lt;br /&gt;
&lt;br /&gt;
このDialogViewModelのインスタンスのセットに合わせて、画面側でダイアログオープンと、クローズを同期するには、 添付ビヘイビアを使用する必要があります（なお、この添付ビヘイビアは、一度作成すれば他のダイアログでも使いまわすことが可能です）。まずは、この添付ビヘイビアをXamlで使用した場合の例から見てみます。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&amp;nbsp;&amp;lt;Window x:Class=&quot;Local.MainWindow&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:local=&quot;clr-namespace:Local&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Title=&quot;MainWindow&quot; Height=&quot;350&quot; Width=&quot;525&quot; x:Name=&quot;This&quot; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;b&gt;&lt;span style=&quot;color: red;&quot;&gt;local:DialogBehavior.Content=&quot;{Binding DialogViewModel}&quot;&lt;/span&gt;&lt;/b&gt;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Window.DataContext&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;local:MainViewModel/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/Window.DataContext&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;b style=&quot;color: red;&quot;&gt;&amp;lt;local:DialogBehavior.ContentTemplate&amp;gt;&lt;/b&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;DataTemplate&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;StackPanel Margin=&quot;30&quot; Width=&quot;300&quot; Height=&quot;200&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;TextBox Text=&quot;{Binding UserName}&quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;TextBox Text=&quot;{Binding Password}&quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Button Content=&quot;ログイン&quot; Command=&quot;{Binding LoginCommand}&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Button Content=&quot;キャンセル&quot; Command=&quot;{Binding CancelCommand}&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/StackPanel&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/StackPanel&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/DataTemplate&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;b style=&quot;color: red;&quot;&gt;&amp;lt;/local:DialogBehavior.ContentTemplate&amp;gt;&lt;/b&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;StackPanel&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;Button Content=&quot;Login Button&quot; Height=&quot;50&quot; Width=&quot;150&quot; Command=&quot;{Binding OpenDialogCommand}&quot; /&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/StackPanel&amp;gt;
&amp;lt;/Window&amp;gt;

&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
添付ビヘイビアは、&lt;b&gt;&lt;span style=&quot;color: red;&quot;&gt;local:DialogBehavior.Content&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: black;&quot;&gt;と&lt;/span&gt;&lt;b style=&quot;color: red;&quot;&gt;local:DialogBehavior.ContentTemplate&lt;/b&gt;&lt;span style=&quot;color: black;&quot;&gt;が&lt;/span&gt;Windowに対して&lt;span style=&quot;color: black;&quot;&gt;指定されています。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: black;&quot;&gt;　Contentの添付プロパティはダイアログを表示する際に使用されるViewModelを指定します。このプロパティにViewModelがセットされている間ダイアログが表示されます。Nullになった場合にはダイアログがクローズします。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: black;&quot;&gt;　ContentTemplate添付プロパティは、ダイアログ表示のためのデータテンプレートです。そのDataContextには、自動的に先ほどのContentのViewModelが設定されるものとします。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
なお、この画面は以下のように表示されます。&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;□メインウィンドウ&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVwp4RPLUwc5bgO9bntYW50p_ZRNeq6Iy36huPmn6ekEe5TEcsn5rdm6HoyiAvBA-DImKgNERXzgwKMuvQcgPhJE3JU8-2mZJ9GQpk9yjFY-0GaXb5qWvAQxy0ehGgI-mC7BZcdMQF4fM/s1600/%25E3%2583%25A1%25E3%2582%25A4%25E3%2583%25B3%25E3%2582%25A6%25E3%2582%25A3%25E3%2583%25B3%25E3%2583%2589%25E3%2582%25A6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;214&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVwp4RPLUwc5bgO9bntYW50p_ZRNeq6Iy36huPmn6ekEe5TEcsn5rdm6HoyiAvBA-DImKgNERXzgwKMuvQcgPhJE3JU8-2mZJ9GQpk9yjFY-0GaXb5qWvAQxy0ehGgI-mC7BZcdMQF4fM/s320/%25E3%2583%25A1%25E3%2582%25A4%25E3%2583%25B3%25E3%2582%25A6%25E3%2582%25A3%25E3%2583%25B3%25E3%2583%2589%25E3%2582%25A6.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
□ダイアログ（メインウィンドウの「Login Button」で起動される）&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8i1uPoO9KxJ68Wqn-5iuw-Ul_qnketDfjGoY-9h-xNeVuvrmObkqqriuVQVXQ2tPUEWOrOUEx1df1wgDsNBrHQoUG5uodoZ8YXC75JQQXLYdOfiTv_-62-RFL6cOJZHbdDxVm-bHKm-A/s1600/%25E3%2583%2580%25E3%2582%25A4%25E3%2582%25A2%25E3%2583%25AD%25E3%2582%25B0.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;234&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8i1uPoO9KxJ68Wqn-5iuw-Ul_qnketDfjGoY-9h-xNeVuvrmObkqqriuVQVXQ2tPUEWOrOUEx1df1wgDsNBrHQoUG5uodoZ8YXC75JQQXLYdOfiTv_-62-RFL6cOJZHbdDxVm-bHKm-A/s320/%25E3%2583%2580%25E3%2582%25A4%25E3%2582%25A2%25E3%2583%25AD%25E3%2582%25B0.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
そして、この添付ビヘイビアの実装は以下の通りです。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: white;&quot;&gt;
&lt;pre&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class DialogBehavior:DependencyObject
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static readonly DependencyProperty ContentTemplateProperty =
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DependencyProperty.RegisterAttached(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;ContentTemplate&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; typeof(DataTemplate),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; typeof(DialogBehavior),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new UIPropertyMetadata(null, (depObj, e) =&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var self = depObj as FrameworkElement;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OnPropertyChanged(self, e);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }));

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static readonly DependencyProperty ContentProperty =
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DependencyProperty.RegisterAttached(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;Content&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; typeof(Object),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; typeof(DialogBehavior),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new UIPropertyMetadata(null, (depObj, e) =&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var self = depObj as FrameworkElement;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OnPropertyChanged(self, e);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }));

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static readonly DependencyProperty DialogProperty =
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DependencyProperty.RegisterAttached(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &quot;Dialog&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; typeof(Window),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; typeof(DialogBehavior));

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static Object GetContent(FrameworkElement element)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (Object)element.GetValue(ContentProperty);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void SetContent(FrameworkElement element, DataTemplate val)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; element.SetValue(ContentProperty, val);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static DataTemplate GetContentTemplate(FrameworkElement element)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (DataTemplate)element.GetValue(ContentTemplateProperty);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void SetContentTemplate(FrameworkElement element, DataTemplate val)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; element.SetValue(ContentTemplateProperty, val);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static Window GetDialog(FrameworkElement element)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (Window)element.GetValue(DialogProperty);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void SetDialog(FrameworkElement element, Window val)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; element.SetValue(DialogProperty, val);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void OnPropertyChanged(FrameworkElement self, DependencyPropertyChangedEventArgs args)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (GetContentTemplate(self) != null)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (GetDialog(self) != null)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CloseDialog(self);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (GetContent(self) != null)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OpenDialog(self);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void OpenDialog(FrameworkElement self)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dialog = new Window();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dialog.WindowStyle = WindowStyle.None;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dialog.ContentTemplate = GetContentTemplate(self);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dialog.Content = GetContent(self);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dialog.SizeToContent = SizeToContent.WidthAndHeight;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetDialog(self, dialog);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dialog.ShowDialog();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private static void CloseDialog(FrameworkElement self)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var dialog = GetDialog(self);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dialog.Close();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetDialog(self, null);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
定型コードが多いのですが、大したことはやっていません。注意点としてあえて言うなら、表示させているダイアログのインスタンスを内部的に保持するためにプライベートの添付プロパティとして「Dialog」を使用していることぐらいでしょうか。&lt;br /&gt;
&lt;br /&gt;
さて、ダイアログ表示を画面状態として扱う方法を見てきました。私の個人的な意見ですが、ダイアログを画面状態として明示的に扱う方法のほうが、ダイアログ表示を十分に抽象化していて連携を行いやすく、Presentation Modelパターンにより準拠していると思われます。今後はメッセンジャーを使用しなくなっていくのではないでしょうか。&lt;br /&gt;
&lt;br /&gt;
今回のサンプルは、まだ実プロジェクトで使用していないため、十分な実績を積んだ方法としてすぐにお勧めすることはできません。あくまで方向性のご提案ですので、その点ご理解いただければ幸いです。&lt;br /&gt;
&lt;br /&gt;
以上。</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/5798670310793127583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/24166670934445279/posts/default/5798670310793127583'/><link rel='alternate' type='text/html' href='http://torokonbu.blogspot.com/2011/10/mvvm.html' title='「MVVMでダイアログ表示のためにメッセンジャーを使用するのはおかしいのでは？」 -ダイアログ表示を画面状態としてモデル化する-'/><author><name>山下　智也</name><uri>http://www.blogger.com/profile/04078632077457392375</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieg6WN5aLSRLd7Q4vHL7CHR9MtfkN0mHaU5n7tsYcM_p3h_d2WGLw0yhdunPeygmKoHiwBI7h80ip-7P5_vWxPzHLVSQAsDFjC8xTBkCypO4qRWYxQYd7d6p3R0lHJ0J4/s75/40_3.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVwp4RPLUwc5bgO9bntYW50p_ZRNeq6Iy36huPmn6ekEe5TEcsn5rdm6HoyiAvBA-DImKgNERXzgwKMuvQcgPhJE3JU8-2mZJ9GQpk9yjFY-0GaXb5qWvAQxy0ehGgI-mC7BZcdMQF4fM/s72-c/%25E3%2583%25A1%25E3%2582%25A4%25E3%2583%25B3%25E3%2582%25A6%25E3%2582%25A3%25E3%2583%25B3%25E3%2583%2589%25E3%2582%25A6.png" height="72" width="72"/></entry></feed>