質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

92.00%

  • C#

    3170questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    329questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

view と viewmodel どっちで処理するか

解決済

回答 3

投稿 2017/02/16 00:54

  • 評価
  • クリップ 0
  • VIEW 288

lazex

score 320

WPF でアプリ作ってると、この処理 view でやるのか viewmodel でやるのかどっちでやるのか迷うことがあります。
特に、別ウィンドウを開く、メッセージダイアログ、ファイルダイアログ、印刷、などの Windows 的なところに触れるもの。

データがほぼ viewmodel にあるので出来る限り viewmodel でやりたいのですが、ときどきコントロールのプロパティが get のみで Binding もできないようになっていて、 viewmodel にデータを持ってくるのが難しいことがあります。
それを取得するために view を viewmodel で持ったり、 view が viewmodel のプロパティにラムダ式代入したりすることになります。

逆にそれらを view でやろうとすると、こちらも引数に渡したいデータは viewmodel にしかなく、 DataContext から直接プロパティ見たりメソッド呼んで取得することになります。

どう書いても綺麗にいかないなー と思ってるのですが、楽にするためにこういう機能ある、とか こうすればいいとかありましたら教えてほしいです。

  • 気になる質問をクリップする

    クリップした質問に回答があった場合に通知・メールを受け取ることができます。

    クリップした質問はマイページの「クリップ」タブからいつでも見ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+6

ViewとかViewModelとか呼んでいるからには、MVVMを前提としていると解釈してよろしいですか?
であれば、ViewとViewModelは疎結合にすべきであって、ViewがViewModelの型を知っていたり、ViewModelがViewを直接参照したりは極力避けるべきであるというのが僕の考えです。

ViewModelがダイアログや別のウィンドウを生成するというのも個人的には好きじゃないです。
Viewのコードビハインド(xaml.cs)を書きたがらない人もいるでしょうが、Viewを作るのはViewが担当するのが一番自然だと思います。

さて、実現方法ですが、ViewModelからViewへの必要な情報の受け渡し自体は、
View側に依存関係プロパティを作ってやって、そこにバインドすることで解決すると思います。

ViewModelを処理の起点としてViewを操作したいときも、同様にプロパティの値変更をトリガとする方法があります。
例として、ViewModelのプロパティ変更をトリガとして別ウィンドウを表示する場合、以下のようになります。

public partial class MainWindow : Window
{
    //子ウィンドウのDataContextプロパティ
    //(型をViewが知る必要はないのでobject)
    public object AWindowContext
    {
        get { return (object)GetValue(AWindowContextProperty); }
        set { SetValue(AWindowContextProperty, value); }
    }

    public static readonly DependencyProperty AWindowContextProperty =
        DependencyProperty.Register("AWindowContext", typeof(object), typeof(MainWindow),
                                    new PropertyMetadata(null, OnAWindowContextChanged));

    private static void OnAWindowContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var me = d as MainWindow;

        //プロパティに新しいオブジェクトが設定されたのをトリガに
        //そのオブジェクトをDataContextとして子ウィンドウを表示する
        if (me.AWindowContext != null)
        {
            var wnd = new AWindow();
            wnd.Owner = me;
            wnd.DataContext = me.AWindowContext;

            wnd.ShowDialog();
        }
    }

    //コンストラクタ
    public MainWindow()
    {
        InitializeComponent();

        //Binding設定
        SetBinding(AWindowContextProperty, "AContext");
    }
}
public class MainWindowViewModel : INotifyPropertyChanged
{
    private AWindowContext _aContext;

    //ウィンドウを表示したいときはここにオブジェクトをセットする
    public AWindowContext AContext
    {
        get
        {
            return _aContext;
        }
        private set
        {
            if (ReferenceEquals(_aContext, value)) return;
            _aContext = value;

            //更新通知
            var h = PropertyChanged;
            if (h != null) h(this, new PropertyChangedEventArgs("AContext"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

何かMVVM支援のフレームワークを使われているのであれば、添付ビヘイビアの仕組みを使うともう少し楽に書けると思います。


別の方法としては(これもMVVMフレームワーク使用が前提になると思いますが)メッセンジャーパターンというのがあります。
これは、ViewModelからViewへの依頼内容をMessegeオブジェクトにパッケージングして、Messengerクラスに通達を依頼する実装パターンです。

これについては↓このあたりがわかりやすいかと思います。
http://tnakamura.hatenablog.com/entry/20110218/mvvm_messenger

投稿 2017/02/16 12:57

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

  • 2017/02/16 23:36

    依存プロパティはユーザコントロールでしか使ってませんでしたが、Windowが自分のVMに対しても使うのですね
    .netの機能だけを使うとただWindow開くだけでかなり長くなっていますが、例えばVMからウィンドウを閉じたいというのであれば、AWindow~のところをクローズ用に同じようなものをもうひとつとなるのでしょうか?

    キャンセル

  • 2017/02/17 00:33

    もう一点質問ですが、現在の私の方法が kiichi54321 さんの解答にある
    > http://sourcechord.hatenablog.com/entry/2016/01/23/170753
    これに近いです。
    (ダイアログはViewModelで直接のときもある)
    クラスではなく、返して欲しい型だけを指定したラムダ式ですけど。

    これだと疎結合ではなく ViewModel の型を知ってることになりますが、このURLの方法よりも疎結合にしたほうが良いメリットはどういうことがありますでしょうか?

    疎結合が良いという話は聞きますが、私の中ではViewとModelは全く依存しない疎結合でいいと思うのですが、ViewとViewModelは名前からしても関連しているものでView専用のデータ処理(setterで個々が変わるとアレ変えてコレ変えてみたいな)をするところなのでそこまで疎結合にする必要はあるのかと疑問に思っています。

    キャンセル

  • 2017/02/17 09:56

    >.netの機能だけを使うとただWindow開くだけでかなり長くなっていますが、例えばVMからウィンドウを閉じたいというのであれば、AWindow~のところをクローズ用に同じようなものをもうひとつとなるのでしょうか?

    同じ方法でやるならば、そうなるでしょうね。モーダルに開いている場合はどちらかというとAWindowContextのほうに作ることになるかと思いますが。
    支援ライブラリを使わずにMVVMしようとすると、どうしてもコード量は多くなってしまう場面が多いですね…。

    >これだと疎結合ではなく ViewModel の型を知ってることになりますが、このURLの方法よりも疎結合にしたほうが良いメリットはどういうことがありますでしょうか?

    ご提示のURLの方法でも、ViewModelに渡すのが実装クラスの型でなくinterfaceならば、依存は限定的になるため、ある程度疎結合であるといえます(ラムダ式でも同じだと思います)
    なのでこの方法でも良いと思いますが、個人的には結局ViewModel側で画面に関わる操作(MessageBox.Show)を呼び出すことになるという点であまり好みではないですね。
    UIスレッドかどうかも意識しないといけないですし。

    >疎結合が良いという話は聞きますが、私の中ではViewとModelは全く依存しない疎結合でいいと思うのですが~
    画面をロジックと切り離したい一番の理由は、ロジックだけでテスタブルな設計にするためです。
    またXAMLで画面を作るWPFのようなものならば、画面だけデザイナーがBlendで作ったりもできますし、表示確認のためにデバッグ用のDataContextを仕込んで動かしたりもできます。

    ViewModelは完全にPOCOで、ViewとModelさえ疎結合ならば良いんだという考え方はそれはそれでありだと思いますが、
    大抵はViewとViewModelを疎結合にできないならば、ViewModelとModelが結合するためにViewとModelも間接的に依存することになってしまうのではないかと思います。

    キャンセル

  • 2017/02/18 02:25

    なるほどです
    参考になりました

    キャンセル

+2

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 14:59

編集 2017/02/16 15:00

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

  • 2017/02/16 23:34

    xamarinについては全く詳しくないのですけど、VMからVを触ることができないような仕組みになってるのでしょうか

    キャンセル

  • 2017/02/17 03:10

    Xamarinは、触ったことないのだけどw、Viewは、ネイティブ実装になるため、それぞれのプラットフォーム、iOS Androidで違います。命令そのものが違う。
    この差を吸収するために、vmのvへの依存をなくして、インターフェースを使った呼び出しにすると、同じVM Mで、違うプラットフォームで動くものが作れます。ただし、viewは、それぞれ作らないとダメです。だから、xamarinは70%のコード共有を謳っている。
    忘れていましたが、単体テストもできるようになるので、わざわざuiをいじってテストするということをしなくてすみます。これは、標準出力をviewにしている、みたいな理解もできると思います。
    VMから、Viewを触ろうなんていう未練を断ち切るいい方法があります。ViewとVMのプロジェクトをわけるのです。そして、VMのプロジェクトをpclにするのです。pclは、いろいろとヘイトが溜まるのですが、いい拘束具になります。
    ついでにちょっとだけでいいので、同じvmを使ってuwpで動くものを作ってみましょう。wpfとuwpは似たようなXaml で記述するのですが、細かいところがいろいろ違います。
    こういうことをすると、vとvmを疎結合にすべきというのが、わかるんじゃないのでしょうか。移植がviewだけになるスタイルってことですね。まわりくどいけど。
    そのため、ターゲットがwpfだけなら、厳密にやる必要はないのです。
    だから、はじめの気持ち悪いという情緒的な表現になってしまうのですが。


    キャンセル

  • 2017/02/17 03:53 編集

    ちゃんと質問に答えていなかった。vmからviewを触るには、インターフェースを使って間接的にやります。直接はありえません。実体は、それぞれで、実装します。
    あと、僕の言っているのは、viewはvmの型を知っていますが、vmはviewを知らない、という構成です。viewをなんでもいいとするためです。c#なんで、インテリセンスが効いて欲しいですし。
    あと、本格的に作った時はどうなるかは、これがとても参考になります。
    https://github.com/PrismLibrary/Prism-Samples-Windows/tree/master/AdventureWorks.Shopper

    キャンセル

  • 2017/02/18 01:04

    View はそれぞれ違うのですね。
    WebのHTMLみたいに共通のもの(XAMLやViewのC#コード)を書けばあとは同じ動きをするようにフレームワークがどうにかしてくれるものだと思ってました。

    キャンセル

-2

回答修正:
Viewモデルはコンポーネントを作るものなので他の方がおっしゃってるように疎結合な実装の方がいいと思いました。ViewModelについて誤解があり無知な回答をしてしまってすみません。誤解を招く回答をしてしまったので回答記述は削除します。

投稿 2017/02/16 02:04

編集 2017/02/16 15:59

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    以下のような回答は評価を下げられます

    • 間違っている回答
    • 質問の回答になっていない投稿
    • 不快な投稿

    評価を下げる際はその理由をコメントに書き込んでください。

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

92.00%

関連した質問

同じタグがついた質問を見る

  • C#

    3170questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    329questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

閲覧数の多いC#の質問