質問
2014年1月22日水曜日 9:38
.NET Framework 4.5/Livet 1.2.0/Visual Studio 2012環境です。
ListBoxItem中にCheckBoxがあり、そのCheckedイベントでCommandをViewModelに通知したいと考えています。
Commandにはバインドしているオブジェクト・・・SelectedItemをパラメータとして渡そうとしています。
とりあえず下記のように書いてみたところCommand通知自体が起きませんでした。
<ListBox ItemsSource="{Binding PersonList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding CheckItemCommand}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
<TextBlock Text="{Binding Name, StringFormat=名前:{0} }" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Age, StringFormat=年齢:{0}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
サンプルコードを作ってみたので何が問題となっているのか教えていただけますでしょうか。よろしくお願いいたします。
(※MVVMおよびLivetの使用は初めてですのでModelやViewModelの設計も間違っているかもしれません。)
http://www1.axfc.net/u/3151601.zip
すべての返信 (14)
2014年1月22日水曜日 12:48 ✅回答済み
CheckItemCommandというコマンドはPersonListに対してどこに配置されていますか?
ListBoxの子要素であるListBoxItemのDataContextは、PersonListの中身(仮にPersonクラスとする)がそれぞれ設定されます。
TextBlockでPathがNameやAgeでバインディングできるのは、バインディングが設定されている位置でのDataContextに設定されているデータがPersonクラスだからです。
つまり、同様にCheckBoxに設定したトリガから見えるのはPersonクラスになっています。
PersonクラスにICommandが実装されているのなら、提示されているコードで実行可能ですが、そうでないのならばCommandが参照するデータを変更してやらないといけません。
<ListBox ItemsSource="{Binding PersonList}" x:Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<!-- 方法1 -->
<i:InvokeCommandAction Command="{Binding DataContext.CheckItemCommand,ElementName=listBox1}"
CommandParameter="{Binding}" />
<!-- 方法2 -->
<i:InvokeCommandAction Command="{Binding DataContext.CheckItemCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
<TextBlock Text="{Binding Name, StringFormat=名前:{0} }" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Age, StringFormat=年齢:{0}}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
class ViewModel
{
public VM()
{
CheckItemCommand = new Cmd();
PersonList = new List<Person>();
PersonList.Add(new Person(){ Name="山田" , Age=20});
}
public ICommand CheckItemCommand { get; set; }
public List<Person> PersonList { get; set; }
}
方法1では、コントロール名を使ってCheckItemCommadが見えるコントロールのDataContextを参照しています。
方法2では、ListBoxItemから親を遡ってListBoxを見つけ出して、ListBoxのDataContextを参照しています。
例としてViewModelに直接PersonListとCheckItemCommandが設定されているとしましたが、深い階層を持っているのであれば、それなりに上位への参照をする必要があります。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
2014年1月23日木曜日 0:57
なるほど。TextBlockがPersonクラスとバインディングされているのに
親のMainWindowViewModelのCommandを直に呼べるわけがないところまでは直ぐに気が付くべきでした。
方法2の方が複雑ですが要素間の依存度が低いという意味では綺麗な書き方に見えますね。
少しMVVMの話に逸れてしまいますが「PersonクラスにViewModelとCommandを実装する」という方法もあるかと思いますが
Livet作者の記事によると
「コレクション・ビュー(=ListBoxやTreeViewなど)の項目ごとに操作があるなら、それらの1項目ごとの用のViewModelも必要です(操作がなくても普通は作ります)。」
とあるので一般的にはこの方法をとるのでしょうか?
ViewModelを分離してしまった際に
例えば「チェックを入れたアイテムの平均年齢を計算してMainWindowViewModelのプロパティに通知したい」というケースでは
ViewModel間の連携をどうするのか気になるところですが、これは「Modelを経由して行う(計算はModel上で)」というのがMVVMの理解として合っているでしょうか。
2014年1月23日木曜日 2:25
>親のMainWindowViewModelのCommandを直に呼べるわけがないところまでは直ぐに気が付くべきでした。
デバッグ実行すると、「出力」にバインドできない部分がエラーとして表示されますので、確認されると良いと思います。バインドがうまくいっていなくてもWPFでは動いてしまうので、うまく動いているように見える場合でも、一応、「出力」を確認されることをお勧めします。
>「コレクション・ビュー(=ListBoxやTreeViewなど)の項目ごとに操作があるなら、それらの1項目ごとの用のViewModelも必要です(操作がなくても普通は作ります)。」
とあるので一般的にはこの方法をとるのでしょうか?
私はそのように作ったことは無いですね。もし、300行あれば300のViewModelのインスタンスが作成されるということですよね。その中にはCommnadも含まれるので、それぞれの項目専用のCommnadのインスタンスが300も作成されることになりますので、無駄に感じます。
私はLivetを使用していないのでわかりませんが、ただ、Livetの仕様上、そのような作りにしないといけないのかもしれませんね。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
2014年1月23日木曜日 13:41
「コレクション・ビュー(=ListBoxやTreeViewなど)の項目ごとに操作があるなら、それらの1項目ごとの用のViewModelも必要です(操作がなくても普通は作ります)。」
とあるので一般的にはこの方法をとるのでしょうか?
コレクションの項目毎のViewModelをどうするかは私も未だに悩みの対象です。
私の場合は、操作が無いなら作りません。あったとしても項目ごとにViewModelを作るのは「Modelには存在しないがViewのために保持しなければならないデータ(例えば今回のように選択のためのCheckBoxの状態など)がある場合」だけですね。
仮想化されるVirtualizingStackPanelなどでは表示範囲外になるとViewModelで保持していないと消えてしまうし、一度も生成されていなければコマンドを設定していても呼ばれない場合があるからです。
項目のクラスにコマンドを実装したところで、複数の項目のコマンドを同時に実行することはあり得ません。
ならば、操作対象となる項目を都度操作用ViewModelに割り当てることで事足ります。(普通は操作対象の項目を選んでから、その項目の中身を操作するのだから)
ついでにCommandParameterで項目を渡すようにすればステートを無駄に増やさずに済みます。
あるいはStaticなRoutedCommandにしてしまえば外側でCommandを捕まえて処理すればいいので項目毎にCommandを作る必要はなくなります。
複数項目をまとめて操作するのであれば、項目クラス毎にViewModelを作る意味はありません。(コレクションか、コレクションを持つクラスのViewModelにCommandを持たせるはず)
さらに言えばDataTableのようなコレクションだと、項目が増減するし、DataTableは間接的にViewに渡されるのでさらにViewModelを差し込むのが大変ですしね。
とはいえ、項目自体が複雑で処理も単体で完結しているクラスで、項目数が少なく、かつ項目数が増減しないのであれば、項目にコマンドを持つViewModelを作ることもあります。
そうすれば項目単体でのみ表示されるようなViewの場合でも違いを意識する必要がなくなるメリットがあります。ViewModelもViewとできるだけ疎な関係にあるよう目指すならば、項目毎に作るということは間違ったアプローチであるとは言えません。
機能を増やさなければいけなくなってもXAMLの変更が少なく済むので、予め項目ごとにViewModelを用意するのもアリです。
現状だと項目毎にViewModelを作らないデメリットに対して作るメリットがそれほど無いので、必要でないなら作らないになってます。
コストの低い方法で項目毎のViewModelを使えるようなフレームワークがあれば変わるかもしれません。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
2014年1月24日金曜日 1:48
gekkaさんがいろいろ書かれていて、私が先に書いたことがあまりにも簡単過ぎたので、もう少し補足しておきます。
まず、gekkaさんが書かれた
>私の場合は、操作が無いなら作りません。あったとしても項目ごとにViewModelを作るのは「Modelには存在しないがViewのために保持しなければならないデータ(例えば今回のように選択のためのCheckBoxの状態など)がある場合」だけですね。
についてですが、私の場合はModelに持たせてしまいます。正確にはModelではなく、Viewにバインドするオブジェクトに持たせるということです。このオブジェクトにModelも含まれています。以下のページで言えば、ModelがDTOで、ViewにバインドするオブジェクトがUIバインド用オブジェクトです。
Part 2. スマートクライアントにおける単体入力データ検;
http://blogs.msdn.com/b/nakama/archive/2009/02/26/part-2.aspx
また、
>そうすれば項目単体でのみ表示されるようなViewの場合でも違いを意識する必要がなくなるメリットがあります。
については、UIバインド用オブジェクトはObservableCollection ですが、項目単体でのみ表示する場合は、UIバインド用オブジェクト[0]をバインドするようにしています。つまり、
単票 - 1つのViewModel - UIバインド用オブジェクト[0]をバインド
リスト - 1つのViewModel - UIバインド用オブジェクトをバインド
と統一し、コードを管理しやすくしています。
実際、私も悩み考えながら現在に至っており、これで良いのかどうかわかりません。ただ、WPFの開発は5年近く行っていますが、今のところリストの項目毎にViewModelを持たせなければ実現できない事例には出会っていませんし、gekkaさんと同じでデメリットの方を大きく感じています。もちろん、これで歩みを止める気はありませんので、機会があれば改善の可能性を探り続けて行くと思います。そこにプログラミングの面白さを感じますしね。
#(追記)単票とリストで1つのViewを構成していることもあります。いわゆるマスター/詳細形式の1つの画面ですね。この場合、単票(Window)という1つのViewに、リスト(UserControl)という1つのViewが含まれる形になっています。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
2014年1月24日金曜日 5:39
私の場合は、操作が無いなら作りません。あったとしても項目ごとにViewModelを作るのは「Modelには存在しないがViewのために保持しなければならないデータ(例えば今回のように選択のためのCheckBoxの状態など)がある場合」だけですね。
お二人の話には理解が追い付かないところも多いのですが、重要なポイントはここだと考えました。
MVVMの考えからいえばModelとViewの構造に差があるならViewModelの中継が必要ということですよね。
私の場合はModelに持たせてしまいます。正確にはModelではなく、Viewにバインドするオブジェクトに持たせるということです。
これはModelとViewModelの境界が曖昧になるほど両者の構造が近いときに成立すると理解します。
差が小さいならばわざわざ分離させる労力の方が大きいということですね。
ただモノによって分離してたりしてなかったりというのは混乱の元になる気もします。
コーディングスタイルの一貫性を保つために必要でなくとも必ずViewModelを用意するのはアリかとも思うのですが
それをするにはフレームワークが力不足なんですかね?そこまで使い込んでないのでよくわかりませんが・・・。
そもそもModelとViewModelをどういう基準で分離してどのように連携させれば良いのか全く分かりません。
ブログ記事などを巡回していてもViewとViewModelの部分に終始しててModelの解釈がバラバラだったりで
ニュアンスは分かるような気もするけど具体的にどうするの?ってのがさっぱりです。
サンプルコードがいくつかあれば良いんですが・・・どなたかお持ちではないでしょうか。
2014年1月24日金曜日 6:18
そもそもModelとViewModelをどういう基準で分離してどのように連携させれば良いのか全く分かりません。
ブログ記事などを巡回していてもViewとViewModelの部分に終始しててModelの解釈がバラバラだったりで
ニュアンスは分かるような気もするけど具体的にどうするの?ってのがさっぱりです。
Hoshinaです
こんにちは
この個所だけに反応します。
どのような場合にも適用可能でかつ優れた方法は無いと割り切ってはどうでしょう。
言葉を換えると,その設計や実装を見れば担当した人のセンスが手に取るように分かるといえます。
設計や実装者の立場からみると,自分のセンスをいかにみがいていくかが大事で,
他人の設計や実装を鑑賞する立場にあれば,いかにそれを楽しむかということのように思います。
それでは
2014年1月24日金曜日 8:22
>MVVMの考えからいえばModelとViewの構造に差があるならViewModelの中継が必要ということですよね。
いえ、私の場合は必ずUI用バインドオブジェクトを用意します。その理由については、先に私が紹介したリンク先に書かれています。
>そもそもModelとViewModelをどういう基準で分離してどのように連携させれば良いのか全く分かりません。
ViewModelはViewとやり取して、画面を実現するものです。ですから、Viewで表示するデータをViewModelは手に入れ、Viewに渡さなければなりません。この渡すオブジェクトがUI用バインドオブジェクトであり、これにModelが含まれます。Modelにはデータベースに含まれる項目もありますし、そうでない項目もあります。
確かにModelの解釈はいろいろあります。それは設計の違いもあるからでしょう。私の場合は、UI用バインドオブジェクト;Modelが、MVVMで言うところのModelに相当するのではないかと思います。
>サンプルコードがいくつかあれば良いんですが・・・どなたかお持ちではないでしょうか。
以下に簡単な例を書いています。
ViewModelクラスにバインドされたDataGridのセル値を更新したい
http://social.msdn.microsoft.com/Forums/ja-JP/b57b13e6-c590-4791-a515-a7986c1227a3/viewmodeldatagrid
また、以下が参考になります。
WPF DataGrid Practical Examples
http://www.codeproject.com/Articles/30905/WPF-DataGrid-Practical-Examples
私の解釈では、このページのThe Architectureの図における、Presentation LayerのCustomerUIObjectsがUIバインド用オブジェクトであり、Data Access LayerにおけるCustomerDataObjectがModelに相当します。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
2014年1月24日金曜日 9:39
>私の場合は、UI用バインドオブジェクト;Modelが、MVVMで言うところのModelに相当するのではないかと思います。
ご指摘のサイトをざっと見たところでは(少し読み飛ばしている部分もありますが)
MVVMにあてはめるとDTO=Model, UIバインド用オブジェクト=ViewModel
に見えます。
データベースを使うアプリケーションならModelは
DBのデータ構造とDBと通信部分、計算処理などを担当する層というのが私の認識です。
尾上さんの定義では「Modelの公開するステートをいちいちラップしてViewに公開するのもViewModelの役目です。」
とあるのでご指摘のサイトで言う"UIバインド用オブジェクト"は正にViewModelに当てはまるように見えます。
これがModel層に含まれるとする根拠がよく分かりません。
>私の解釈では、このページのThe Architectureの図における、Presentation LayerのCustomerUIObjectsがUIバインド用オブジェクトであり、Data Access LayerにおけるCustomerDataObjectがModelに相当します。
それは同意です・・・が、ViewModelはプレゼンテーション層に属するはずでやはりUIバインド用オブジェクト=ViewModelの解釈でいいように思います。
私の理解の根本部分が間違っていたり「私の解釈はLivetとは異なる」という点があればご指摘くださればと思います。
※少々タイトルとは別件でスレが伸びてしまっていますが折角なのでもう少し続けさせてください。
2014年1月25日土曜日 17:02
すみません。遅くなりました。
さて、ViewModelはバインドするオブジェクトではありません。ViewのDataContextに設定するのが普通ですし、私もほぼそのようにしています。
ViewModelはViewのコントロールに対して、バインド用のオブジェクトを提供します。一般的には、ObservableCollectionです。
おそらく、_Matsuさんは、ObservableCollectionはModelを利用してViewModelで作成するので、実際にバインドするオブジェクトを生成するViewModelこそがUIバインド用オブジェクトだと思われたのではないでしょうか?
しかし、実際には、上で挙げたCodeProjectの例でいえば、UIバインド用オブジェクト(DTO)をコレクションとしたObservableCollectionは、ViewModelではないクラスで生成されます。ViewModelは単に、ObservableCollectionを受け取り、Viewにバインドを中継しているだけです。
では、このObservableCollectionを作成するクラスを切り出した時、このクラスはViewModelに属するのでしょうか? Modelに属するのでしょうか?
実際、ここは私にははっきりとはわかりません。MVVMは概念に過ぎず、具体的なクラスの構成を定義しているわけではないからです。ただ、私は、MVVMの概念でいうところのViewModelは、1つのクラスで十分だと思うのです。なぜなら、このViewModelはModelとViewとの橋渡し役をするだけであり、どのようなデータを用意するのかはModelの役割だと思うからです。よって、Modelはどのようなクラス群から構成されるかはまちまちになるでしょう。それは扱うデータが変わるからですし、ビジネスロジックも変わるからです。
先のCodeProjectの例で言えば、ViewおよびViewModelに相当するクラスはそれぞれ1つです。その他、たくさんのクラスがありますが、それらはMVVMで言うところのModelになるでしょう。UIバインド用オブジェクトはCustmerUIObjectになります。分類的にはPresentation Layerになっていますが、だからといってViewModelということではなく、ViewModelとは別物です。Model層がPresentation Layerも一部担っているということだと思います。あ~、書き方が悪いですね。Presentation Layerで使用するオブジェクトを提供する機能を有しているという方が誤解が少ないと思います。そして、Presentation LayerであるViewModelがそれをどう料理するかは完全に自由なわけです。
MVVMは概念であり、実際にはVew, ViewModel, Modelの3つのクラスからのみ明確に構成されるわけではありません。MVVMの概念を実現するために、その概念に沿って明確にクラスを定義できないケースもあるように思います。先のCodeProjectの例で言えば、CustmerObjctDataProviderは、ViewModelの一部なのか、Modelの一部なのかということです。
極端な話、ViewModelという一つのクラスに、Modelの機能も書いてしまってはどうでしょうか? コードの書き方としては保守しにくいかもしれませんが、MVVMの概念は実現できています。結局、MVVMの概念さえ実現できていれば、どのようなクラス構成にするのかは完全に自由なのだと思います。もちろん、そのクラス構成に良し悪しはあります。
ですから、先のCustmerObjctDataProviderクラスが、ViewModelなのか、Modelなのかは本質的な問題ではないと思うのです。本質的ではないことを問題にするため、Modelがあやふやになり、ネット上にもいろいろな情報があって混乱するのでしょう。
いずれにしてもこのCodeProjectのサンプルコードを是非、眺められることをお勧めします。このサンプルコードは私にとって多大な影響を与えたコードです。
#私も完全に自信を持っているわけではありませんし、むしろ誤っている、そこは違うという意見を待っていますので、忌憚なく指摘していただければ幸いです。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
2014年1月27日月曜日 2:28
丁寧な説明ありがとうございます。
MVVMは概念であり、実際にはVew, ViewModel, Modelの3つのクラスからのみ明確に構成されるわけではありません。MVVMの概念を実現するために、その概念に沿って明確にクラスを定義できないケースもあるように思います。先のCodeProjectの例で言えば、CustmerObjctDataProviderは、ViewModelの一部なのか、Modelの一部なのかということです。
むしろ3つの層に明確に分離することがMVVMを導入するメリットなのではないでしょうか。
CustmerObjctDataProviderはやはりViewModelの一部であると思います。
ObservableCollectionのアイテムのクラスがModelのクラスを使うかViewModelのクラスを使うかで言えば本来はViewModelのクラスを使うはずです。
ここにModelのクラスを直接使えてしまうのはViewの構造とModelの構造が一致しているケースに限ります(例えばModelはアイテム毎に複数の金額の情報を有しているがViewに合計金額しか表示しない場合、合計金額のプロパティしか有しないViewModelを用意するはず。)。
質問は「ViewとModelの構造が一致するようなケースでもViewModelは一般的には実装する?」という点でgekkaさんの回答は「デメリットが大きいので私はしない」というものだと思います。
またViewModelとModelがそれぞれ複数に及ぶような複雑な構成の場合、
それぞれのインスタンス参照や通知の仕組みをどのように実装するのかを知りたく具体的なサンプルを求めておりました。
trapemiyaさんの解釈はModelとViewModelの境界が曖昧だと感じてしまうのです。
CodeProjectの例では作者は図に示すDataAccessLayerとPresentationLayerとが
明確にビジネス層とプレゼンテーション層の境界であると認識されているように思いますし
この考えはMVVMのModel-ViewModelの境界の考えと一致していると思います。
2014年1月27日月曜日 7:51
>むしろ3つの層に明確に分離することがMVVMを導入するメリットなのではないでしょうか。
私もそう思います。3つの層があいまいというわけではなく、3つの層はあるのだけれど、どちらの層にあっても良いように思えるクラスが存在しうるということを言いたいのです。例えば、バインド用オブジェクト(以後、UIObject)が、あるViewに特化したもの、つまりあるViewModelに特化した形式だとすれば、これはViewModel層だと考えるのが自然でしょう。しかり、Modelクラスと全く同じ項目を持ったUIobjectクラスだとしたらどうでしょうか? UIObjectクラスはModelクラスの代わりをしていると思えませんか? しかも、UIObjectはViewModelに関して完全に疎です。つまり、複数の異なるViewModelに共通して使うことができます。また、UIObjectは項目間のエラーチェックなどのビジネスロジックを持ちえます。これはViewModelとは完全に疎であり、Model層で実装することは理にかなっています。UIObjectの作りによって、ViewModel層寄りなのか、Model層l寄りなのかが変わると思うのです。本来、MVVMには無いUIObjectです。しかし、どこかの層に属させなければならないとすれば、それをどちら寄りに作るのかによるのだと思いますが、どちらの層でも良いような場合があり、それに白黒付けるのはあまり意味がないのではないかと思ったのは、先に述べた通りです。
>ObservableCollectionのアイテムのクラスがModelのクラスを使うかViewModelのクラスを使うかで言えば本来はViewModelのクラスを使うはずです。
一般的には、ObservableCollectionのアイテムのクラスは、ViewModelから使われることはあっても、ViewModelのクラスを使うことはないと思います。もし、使うのであれば、ObservableCollectionのアイテムのクラスは、ViewModelの参照を持っていなければなりません。もし、このような構造の場合、ViewModelがModelに対して短命な場合、メモリーリークを引き起こしてしまうでしょう。
>ここにModelのクラスを直接使えてしまうのはViewの構造とModelの構造が一致しているケースに限ります(例えばModelはアイテム毎に複数の金額の情報を有しているがViewに合計金額しか表示しない場合、ViewModelは合計金額のプロパティしか有しないViewModelを用意するはず。)。
ごめんなさい。ここの意味がよくわかりませんでした。Modelと同じ構造のObservableCollectionを用意しても意味が無いから用意しないということでしょうか?いずれにしても、
>CustmerObjctDataProviderはやはりViewModelの一部であると思います。
と書かれている理由のなかにUIObjectが全く出てきていないのですが、CustmerObjctDataProviderはModelではなく、UIObjectを扱うクラスですので、UIObjectとの関連はどうなんだろう?と疑問に思いつつ、今一歩、おっしゃられていることが理解できていません。もう少し詳しく説明していただけれると幸いです。
>質問は「ViewとModelの構造が一致するようなケースでもViewModelは一般的には実装する?」という点でgekkaさんの回答は「デメリットが大きいので私はしない」というものだと思います。
gekkaさんの回答は、例えばリストの1行毎にバインドする場合、その1行毎にViewModelを用意するかどうかだと思いますし、私もそれを前提に書き込みましたが・・・
>またViewModelとModelがそれぞれ複数に及ぶような複雑な構成の場合、
それぞれのインスタンス参照や通知の仕組みをどのように実装するのかを知りたく具体的なサンプルを求めておりました。
これに関しては、バインドやMVVMのデザインパターンとは離れますので、普通のクラス間の連携で良いと思います。実際、私も、マスター/詳細画面において、二つのViewModelを連携させています。ただ、先にも述べましたがメモリーリークに気を付ける必要があります。
>trapemiyaさんの解釈はModelとViewModelの境界が曖昧だと感じてしまうのです。
CodeProjectの例では作者は図に示すDataAccessLayerとPresentationLayerとが
明確にビジネス層とプレゼンテーション層の境界であると認識されているように思いますし
この考えはMVVMのModel-ViewModelの境界の考えと一致していると思います。
サンプルコードに照らし合わせてみると、CodeProjectの図に関しては、_Matsuさんの言われる通りだと思います。UIObjectはModelを拡張して、Viewへのバインドに特化したオブジェクトとして存在しています。ですから、プレゼンテーション層と言えるのだと思います。しかし、本質はModelであり、Modelが形を変えたものと考えられます。では、このサンプルコードにない、エラーチェックなどのビジネスロジックが発生した場合、どこに書けば良いでしょうか? MVVMではビジネスロジックはModelに書きますから、Modelの代わりにバインドしているUIObjectに書くことが自然でしょう。そうすれば、UIObjectはModel層としての側面も持ってしまうことになります。
もちろん、UIObjectには書かないという選択肢もあると思いますが、UIObjectを明確にプレゼンテーション層に保つためだけに行うのは、無駄が多いと思うのです。
う~ん、なかなか難しいところですね。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
2014年1月27日月曜日 10:33
>そうすれば、UIObjectはModel層としての側面も持ってしまうことになります。
>もちろん、UIObjectには書かないという選択肢もあると思いますが、UIObjectを明確にプレゼンテーション層に保つためだけに行うのは、無駄が多いと思うのです。
trapemiyaさんに対して私が言わんとしてるところは正にここで
UIObjectにViewModelとModelを兼用させるのは楽なのは分かるんだけど
ここの分離を徹底しないと長い目で見るとメンテナンス性が下がるでのはという疑問です。
ViewとViewModelの分離にしてもコードビハインドとViewModelに実装が散らばっていると
メンテナンス性が悪いのでコードビハインドを止めてViewModelに統一したいですよね。
同じことがModelとViewModelの分離にも言えるのではないかと思うわけです。
どうも分離する方が大変という意見が多いようですがこれは経験豊富な方の意見ならそうなのかな、と思います。
(現状、という話なのでいずれフレームワークが解決してくれるのかな)
で、本題はObservableCollectionの項目クラスのViewModelを作るかどうかでしたが
これについてもデメリットが大きいのでしない、ということでしたが
作ってみた場合はどんな実装になるのか知りたいです。
デメリットを承知でやってみたいのはModel-ViewModelの分離の徹底を実践してみたいからです。
MainWindowViewModelにObservableCollection<ViewModel.Person>という参照を持たせてしまったら
Model側のObservableCollection<Model.Person>に対してどう変更を通知するのかイメージが湧きません。
下手な実装をするとご指摘のとおりメモリリークの可能性も出てきますし。
2014年1月28日火曜日 4:08
>UIObjectにViewModelとModelを兼用させるのは楽なのは分かるんだけど
ここの分離を徹底しないと長い目で見るとメンテナンス性が下がるでのはという疑問です。
あ~、そうか。ごめんなさい。前提条件がずれてますね。CodeProjectのサンプルはMVVMではないので、私はそのサンプルを参考にしてMVVM化しています。よって、UIObjectクラスとは別にViewModelクラスが存在します。そのため、UIObjectは完全にModel層に分類しています。UIObjectはModelから作られるViewを意識したオブジェクトですので、プレゼンテーション層とも言えなくはないと思いますが、逆に言えばModelをベースに作成しているのでModel層とも言えますし、それを否定する理由もありません。また、UIObjectにはビジネスロジックを記述することが普通ですので、こうなるとModel層に分類することは決定的になります。つまり、以下のような構造になります。
View - ViewModel - UIObject(Model)
>MainWindowViewModelにObservableCollection<ViewModel.Person>という参照を持たせてしまったら
Model側のObservableCollection<Model.Person>に対してどう変更を通知するのかイメージが湧きません。
言わば、リストの1行毎にMVVMを適用するわけですから、ViewModel.PersonがModelへの参照を持っているはずであり、よってバインドなどを使い、通知も簡単にできるように思います。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/