WPF でアプリ作ってると、この処理 view でやるのか viewmodel でやるのかどっちでやるのか迷うことがあります。
特に、別ウィンドウを開く、メッセージダイアログ、ファイルダイアログ、印刷、などの Windows 的なところに触れるもの。
データがほぼ viewmodel にあるので出来る限り viewmodel でやりたいのですが、ときどきコントロールのプロパティが get のみで Binding もできないようになっていて、 viewmodel にデータを持ってくるのが難しいことがあります。
それを取得するために view を viewmodel で持ったり、 view が viewmodel のプロパティにラムダ式代入したりすることになります。
逆にそれらを view でやろうとすると、こちらも引数に渡したいデータは viewmodel にしかなく、 DataContext から直接プロパティ見たりメソッド呼んで取得することになります。
どう書いても綺麗にいかないなー と思ってるのですが、楽にするためにこういう機能ある、とか こうすればいいとかありましたら教えてほしいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
ベストアンサー
ViewとかViewModelとか呼んでいるからには、MVVMを前提としていると解釈してよろしいですか?
であれば、ViewとViewModelは疎結合にすべきであって、ViewがViewModelの型を知っていたり、ViewModelがViewを直接参照したりは極力避けるべきであるというのが僕の考えです。
ViewModelがダイアログや別のウィンドウを生成するというのも個人的には好きじゃないです。
Viewのコードビハインド(xaml.cs)を書きたがらない人もいるでしょうが、Viewを作るのはViewが担当するのが一番自然だと思います。
さて、実現方法ですが、ViewModelからViewへの必要な情報の受け渡し自体は、
View側に依存関係プロパティを作ってやって、そこにバインドすることで解決すると思います。
ViewModelを処理の起点としてViewを操作したいときも、同様にプロパティの値変更をトリガとする方法があります。
例として、ViewModelのプロパティ変更をトリガとして別ウィンドウを表示する場合、以下のようになります。
C#
1public partial class MainWindow : Window 2{ 3 //子ウィンドウのDataContextプロパティ 4 //(型をViewが知る必要はないのでobject) 5 public object AWindowContext 6 { 7 get { return (object)GetValue(AWindowContextProperty); } 8 set { SetValue(AWindowContextProperty, value); } 9 } 10 11 public static readonly DependencyProperty AWindowContextProperty = 12 DependencyProperty.Register("AWindowContext", typeof(object), typeof(MainWindow), 13 new PropertyMetadata(null, OnAWindowContextChanged)); 14 15 private static void OnAWindowContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 16 { 17 var me = d as MainWindow; 18 19 //プロパティに新しいオブジェクトが設定されたのをトリガに 20 //そのオブジェクトをDataContextとして子ウィンドウを表示する 21 if (me.AWindowContext != null) 22 { 23 var wnd = new AWindow(); 24 wnd.Owner = me; 25 wnd.DataContext = me.AWindowContext; 26 27 wnd.ShowDialog(); 28 } 29 } 30 31 //コンストラクタ 32 public MainWindow() 33 { 34 InitializeComponent(); 35 36 //Binding設定 37 SetBinding(AWindowContextProperty, "AContext"); 38 } 39} 40
C#
1public class MainWindowViewModel : INotifyPropertyChanged 2{ 3 private AWindowContext _aContext; 4 5 //ウィンドウを表示したいときはここにオブジェクトをセットする 6 public AWindowContext AContext 7 { 8 get 9 { 10 return _aContext; 11 } 12 private set 13 { 14 if (ReferenceEquals(_aContext, value)) return; 15 _aContext = value; 16 17 //更新通知 18 var h = PropertyChanged; 19 if (h != null) h(this, new PropertyChangedEventArgs("AContext")); 20 } 21 } 22 23 public event PropertyChangedEventHandler PropertyChanged; 24}
何かMVVM支援のフレームワークを使われているのであれば、添付ビヘイビアの仕組みを使うともう少し楽に書けると思います。
別の方法としては(これもMVVMフレームワーク使用が前提になると思いますが)メッセンジャーパターンというのがあります。
これは、ViewModelからViewへの依頼内容をMessegeオブジェクトにパッケージングして、Messengerクラスに通達を依頼する実装パターンです。
これについては↓このあたりがわかりやすいかと思います。
http://tnakamura.hatenablog.com/entry/20110218/mvvm_messenger
投稿2017/02/16 03:57
総合スコア425
0
VMからVを触る書き方は気持ち悪い。
今時の、xamarinのような複数のプラットフォームで動くもの作ることも念頭に置くとあり得ない実装。
VMとMの役割がだんだんわからなくなるなら同意しますが。
VMからVは、メッセージパターンを使うのが定番だったのですが、
メッセージパターンは古いやり方っぽくて、ドキュメントがある時点から一気に無くなる。
今時はDIを使い、こう作るみたいです。
http://sourcechord.hatenablog.com/entry/2016/01/23/170753
あんまり関係ないのですが
https://www.codeproject.com/tips/478643/mouse-event-commands-for-mvvm
は、結構便利です。
似たようなアプローチで、データバインドできないものができたりします。
投稿2017/02/16 05:59
編集2017/02/16 06:00総合スコア1984
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/02/16 18:10
2017/02/16 18:53 編集
2017/02/17 16:04
0
回答修正:
Viewモデルはコンポーネントを作るものなので他の方がおっしゃってるように疎結合な実装の方がいいと思いました。ViewModelについて誤解があり無知な回答をしてしまってすみません。誤解を招く回答をしてしまったので回答記述は削除します。
投稿2017/02/15 17:04
編集2017/02/16 06:59退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/02/16 14:36
2017/02/16 15:33
2017/02/17 00:56
2017/02/17 17:25