質問
2016年1月28日木曜日 4:33
現在、VS2013、C#、MVVMを用いてアプリケーションの開発を行っています。
ViewModelからViewを表示するサンプルはネット上にたくさん情報があるのですが、その際表示するViewのOwnerの設定方法がわかりません。
下記のようにViewModelにWindowを持たせてViewを呼び出す際に設定する方法で問題ないのでしょうか?
よろしくお願いします。
public class ViewModel
{
private Window _window;
public ViewModel(Window window)
{
this._window = window;
}
public void ShowWindow()
{
var fooWindow = new FooWindow
{
Owner = this._window
};
fooWindow.ShowDialog();
}
}
すべての返信 (8)
2016年1月28日木曜日 6:50 ✅回答済み
下記のようにViewModelにWindowを持たせてViewを呼び出す際に設定する方法で問題ないのでしょうか?
一般論で言えば、問題ありになると思います。しかし、これはどのような環境やポリシーで開発されているのかによると思います。私的には提示されているコードは絶対にダメだとは思いません。理由は、ViewModelでWindowのインスタンスに対して、何ら操作をしていないからです。WindowのインスタンスからWindowにあるコントロールを操作するのはさすがにNG感が強くなります。Windowのインスタンスがあるとこのようなコードを書けてしまいますので、将来的にこれを防止したいということであれば、このようにWindowのインスタンスを持たせることは止めた方が良いでしょう。もし、ご自分一人で開発されており、この辺りの事情がわかっているという前提であれば、先ほど述べたように絶対ダメだとは思いません。
さて、前述したNG感に戻って、なぜそう感じたかを述べます。
そもそもViewとViewModelを設ける理由は何でしょうか? 以下の理由が多く取り上げられていると思います。
1.ウインドウのデザインと分離できる。デザイナーさんと別に仕事が進められる。
2.単体テストのしやすさ。
Winodowのインスタンスを持てば、Windowの操作ができてしまいますので、そういう記述をされれば、デザイナーさんとの分業も単体テストもとたんにやりにくくなる可能性があります。要するにViewとViewModelが密な関係になるので、NG感を感じたのです。しかし、必ずしもNGではないと思いますので、NG感という表現を使いました。
1.について言えば、分業せずに自分でウインドウも作るのであれば、このデメリットは消失します。
2.の単体テストは、それが必要な部分を別クラスにしてテストすれば済みことが多いでしょう。今回の質問ではViewModelがWindowのインスタンスを持っていますが、仮にViewModelで単体テストをするにしても、WindowのインスタンスはダイアログのOwnerに使用しているだけですので、問題ないと思われます。(つまり、Windowsのインスタンスはnullでもかまわない)
これらの理由により、最初に述べたように私は絶対ダメだとは思いませんでした。
しかしだからと言って、ViewModelがViewのインスタンスを持つことを推薦しません。おそらく私であればBehaviorを継承したクラスを作成し、そこで処理をさせるようにすると思います。やはり、ViewModelがViewのインスタンスを持つということは、ViewとViewModelがより濃厚な密の関係に発展する可能性があるということであり、そういう芽を摘み取っておきたいからです。
また別の理由として、おそらく個人や小規模の開発において一番メリットを受けるのは、上にあげた1.でも2.でもなく、特定のパターンで構築されることによって、どのソースも構造が統一され、保守や改修がしやすく、また新規開発においても以前のソースがテンプレートのように使えるということだと感じているからです。Windowのインスタンスを持たないというMVVMの一般的だと思われるポリシーの元で開発すれば、ソースによってぶれることがありません。
最後になりますが、私が一番言いたかったのは、どのようなポリシーで構築する場合でも、その背景を理解、検討した上で進めていくべきだということです。MVVMを必ずしもきっちり守る規則や義務はありません。しかし、MVVMには先人の知恵が詰まっています。それらに敬意を払い理解しつづけようと努力を続けることは、自分のステップアップに必ずつながると思います。
以上、あくまで個人的な感想ですので、多くの人に耳を傾けて、ご自分の言葉で咀嚼されることをお勧めします。
開発におけるポリシーを決められるのは、最後は当事者以外にありません。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
2016年1月28日木曜日 4:36
こんにちは。
ViewModelでView上のインスタンスを保持するのは好ましくないと思います。
どのようにViewModelからViewを表示させているかによりますが、
View側でOwnerを設定する仕組みにするべきだと思います。
2016年1月28日木曜日 7:15
私のアプリでは、すべてのWindowはMainWindow直下の子ウィンドウとして設計しているので、下記のようにしておりました。
MyWindow.Owner = App.Current.MainWindow;
Ownerに設定するウィンドウは、使い勝手から判断してみると答えが出るかと思います。
常にメインウィンドウの中央に出すのが使いやすいのであれば、OwnerをメインウィンドウにしてCenterOwnerを設定する必要があります。
MyWindow.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
アプリの用途からして、場合によってはデスクトップの中央に出したいかもしれないし、ツールチップのように各コントロールからの相対位置に出したい場合もあります。
ここまで来たら、Viewでやった方がスマートか、ViewModelでやった方が手数が少なくわかりやすいか、というコーディングポリシーを検討すればよいでしょう。
2016年1月28日木曜日 8:16
>Tak1waさん
回答ありがとうございます。
>View側でOwnerを設定する仕組みにするべきだと思います。
これは例えばViewModelをバインドするMainWindowが存在したとして、MainWindowのメソッドでFooWindowを
呼び出すイメージでしょうか?
2016年1月28日木曜日 8:23
MainWindowでもいいですが、そもそもの画面遷移の実装方針に依ります。
ViewModelがMessengerを経由してViewへメッセージを送信し、
ViewがTriggerActionを使って画面遷移しているのであれば、その時にOwnerを指定すると思います。
WindowをShowするのかShowDialogするのかなどをView側で吸収するケースと同じだと考えています。
2016年1月28日木曜日 8:48
>trapemiyaさん
回答ありがとうございます。
今は個人で開発していますが、いずれ他者が触る可能性もあるので、保守・改修がしやすいコーディングを目指しております。
そうなると、trapemiyaさんが仰るとおり『MVVMの一般的だと思われるポリシー』で開発したいと思うのですが、
WPF、MVVMがなかなか理解できずに四苦八苦しております。
Behaviorも含め、もう少し検討したいと思います。
貴重なご意見ありがとうございました。
2016年1月28日木曜日 8:59
>Tak1waさん
回答ありがとうございます。
MainWindowでボタンを押下したらCommandでFooWindowを表示する処理を書いております。
まだWPF、MVVMともに勉強しつつアプリケーションの開発行っていますので、MessengerやTriggerActionをしっかりと理解できていない状況です。
2016年1月28日木曜日 9:18
>NIM5さん
回答ありがとうございます。
『手数が少なくわかりやすいか』これ大事ですよね。
他の方のご意見も参考にもう少し考えたいと思います。