次の方法で共有


ViewModelのインスタンス化の方法

質問

2016年1月29日金曜日 5:13

現在MVVM、WPFを勉強しているのですが、いくつか悩む点があるので質問させていただきます。
疑問点1:ViewModelのインスタンス化についてですが、調べていくと大体下記のa、b、c、dの4種類あると思いますが、どれが一般的なのでしょうか?
疑問点2:ViewModelのコンストラクタに引数があり、かつ引数は呼び出し元で設定される場合、b、cの場合はViewModelのインスタンス化がViewの外側なので問題ないと思うのですが、a、dの場合はどのようにすればいいのでしょうか?

  • a
public UserListView()
{
    InitializeComponent();
    DataContext = new UserListViewModel();
}
  • b
public UserListView(UserListViewModel userListViewModel)
{
    InitializeComponent();
    DataContext = userListViewModel;
}
  • c
var userListView = new UserListView();
var userListViewModel = new UserListViewModel();
userListView.DataContext = userListViewModel;
  • d
<Window.DataContext>
    <local:UserListViewModel />
</Window.DataContext>

疑問点3:UserListViewModelが下記のようなクラスの場合で、UserListViewModel.GetUser()メソッドが成功時のみUserListViewを表示して、失敗した場合はMessageBoxでエラー表示してUserListViewを表示しない場合、上記a、dの場合はどのようにすればいいのでしょうか?
b、cの場合は外側でインスタンス化した後にUserListViewModel.GetUser()メソッドを呼び出し、失敗したらMessageBoxでエラー表示と考えています。

// UserListViewはUserListViewModel.UserListを一覧表示する

public class UserListViewModel
{
    public List<User> UserList { get; set; }
    
    public bool GetUser(int roleId)
    {
        // 指定されたroleIdを元にDBからユーザーの取得
        // 成功時はtrue、失敗時はfalseを返す
    }
}

初歩的な質問になってしまうのですが、どなたかご教示いただけると助かります。
よろしくお願いします。

すべての返信 (4)

2016年1月29日金曜日 5:41 ✅回答済み

こんにちは。

疑問点1:ViewModelのインスタンス化についてですが、調べていくと大体下記のa、b、c、dの4種類あると思いますが、どれが一般的なのでしょうか?

dをよく見ますが、UserListViewがどういう位置づけの部品なのかによって扱い方は変わると思います。
UserListViewはWindowですか?外部からパラメータを受け取りますか?
MVVMであればModelを経由してパラメータ渡しが出来れば一番良いと私は思いますが。

疑問点2:ViewModelのコンストラクタに引数があり、かつ引数は呼び出し元で設定される場合、b、cの場合はViewModelのインスタンス化がViewの外側なので問題ないと思うのですが、a、dの場合はどのようにすればいいのでしょうか?

aについては、そもそもどうパラメータを受け取るかの問題なので回答が難しいですね。
何かを経由して外部からパラメータを渡すことになると思いますが、
dは引数付コンストラクタは無理だと思い込んでたのですが、ObjectDataProviderを使えば出来ました。

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <!--<local:MainWindowViewModel />-->
        <ObjectDataProvider ObjectType="local:MainWindowViewModel">
            <ObjectDataProvider.ConstructorParameters>
                <sys:String>ABC</sys:String>
            </ObjectDataProvider.ConstructorParameters>
        </ObjectDataProvider>
    </Window.DataContext>
    <Grid>
        <TextBlock Text="{Binding MyName}" />
    </Grid>
</Window>

疑問点3:UserListViewModelが下記のようなクラスの場合で、UserListViewModel.GetUser()メソッドが成功時のみUserListViewを表示して、失敗した場合はMessageBoxでエラー表示してUserListViewを表示しない場合、上記a、dの場合はどのようにすればいいのでしょうか?

ビジネスロジック(?)的なものがViewModel層に実装されているのが少し気になりますが…。
View / ViewModelで「初期化」の呼び出しのみを実装し、Modelのメイン処理を呼び出したらひとまず仕事は終了です。
どういう処理のときにどうすれば良いかというタイミングや処理はあまり意識せず、ViewやViewModelは、ModelのUserOK(仮)の通知がtrueであればUserListViewを表示、falseであればUserListViewを非表示かつエラーメッセージを表示する、というTriggerだけ実装してやれば良いと思います。

<追記>
お約束ですが、こう実装しなきゃいけない!という意図ではありませんので…
私ならこうするかなという1意見と捉えてください。


2016年1月29日金曜日 7:04 ✅回答済み

疑問点1について

どれが一般的ということはないと思いますが、私の場合ですと一番多く使うのはcです。理由は、ViewとViewModelを結びつける関連付けが局所的に書けるのと、C#のコードのみで書け、応用が効きやすいからです。
一方で、初期画面や簡単なアプリケーションの場合は、dも使います。
また、第5の方法として、ViewModelからViewを関連付ける方法もあります。以下のページの「ViewModel に View を適用する」をご覧ください。

Model-View-ViewModel デザイン パターンによる WPF アプリケーション
https://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx

あ~、あと第6の方法、WindowタグのDataContextで指定することもできますね。

疑問点2について

aはViewがどのようにインスタンス化されるのかにもよりますが、基本的にはViewがインスタンス化される際にViewModelのコンストラクタの引数が必要になるわけですから、Viewをインスタンス化する際に何らかの方法でその引数を渡す必要があります。dについても全く同じで、ただXaml側に移っただけの話です。
dについては既にTak1waさんが書かれているようにXaml側で引数を指定することができます。aについてもXaml側で引数をViewのプロパティの形で与えれば渡すことができると思います。あとはコンストラクタの引数ではありませんが、ビヘイビア経由で渡すことも可能でしょう。
しかし、渡す引数はハードコーディングされたものになります。求めているのはこのようなことでしょうか? おそらくそうではなく、ViewModelのコンストラクタに渡す引数を条件によって変更したいという要求ではないでしょうか? こうなるとやってやれなくはないかもしれませんが、いずれにしても複雑になると思います。よって、実用上は難しいんじゃないかと思いますし、無理にやらなくてもいいんじゃないかとも思います。これらのことから、a,b,c,dの使い分けが見えてくるのではないでしょうか?

疑問点3について

こちらについては疑問点がよくわかりませんでした。いずれの方法でもViewModelがインスタンス化されるわけですから、GetUserメソッドを実行し、その後の処理をするのは同じViewModelなので違いはないんじゃないかと思います。私が考え違いしているかもしれませんので、これについて補足していただければと思います。
それよりも気になったのは、ViewModelでMessageBoxを表示することです。一般的にViewModelではUIが絡むようなことは行いません。そのためにViewと分離されています。もっともこれも、そういうコードを書いたから即座にまずくなるというのではなく、UIとどれだけ疎にするのかの度合いと、コーディング技量を比較して判断してもいいんじゃないかと個人的には思います。0か1ではなく、0.9ぐらいの疎でもいいじゃないということです。

以上、あくまで個人的な感想ですので、こうしなければならないということではありませんので、ご注意ください。

★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/


2016年1月29日金曜日 9:21

Tak1waさん

ありがとうございます。

疑問点1に関してはアプリケーションの設計次第でどれを使用するかが変わるということですかね。

疑問点2に関しても設計によりけりですかね。

疑問点3のビジネスロジック?とはGetUser()メソッドのことでしょうか?

Triggerに関してもまだ勉強中なのでよくわからないのですが、調べてみたいと思います。

貴重なご意見ありがとうございました。


2016年1月29日金曜日 9:42

trapemiyaさん

ありがとうございます。

疑問点1,2はアプリケーションの設計次第だと思うので、そのあたりで判断したいと思います。

また、引数に関してはtrapemiyaさんの仰るとおりの仕様なので、その辺もふまえて考えてみます。

疑問点3についてはViewを開こうとする→表示するデータ取得失敗→エラー表示の流れをMVVMでやるにはどうすればいいのかがわからないのです。

また、ViewModelでUIが絡むのはMVVMでは一般的にNGだとは知っているのですが、なにぶん勉強中ですのでまだ分離できない状況です。