MVVM Show View パターン1 Viewのコードビハインド


Viewを表示するパターンを考える

MVVMを勉強しはじめた時に、いったいコードをどうかけばいいのかさっぱり分からなかったのことは、Viewを表示するコードをどこに書くのか?
今でもそれらが曖昧なので、明確にしておくためにこの記事を書きます。
ただし、調べると、より分からない点がさらに増えました。
フィードバックを頂けるとありがたいです。

Newする順序

V・VM・MをNewする順序も、パターンによって異なります。それにも留意します。

結合・参照?

MVVMでは、ModelはViewModelを知らないし、ViewModelはViewを知らない、ことが前提です。
この知らない、というのは、名前を知っているだけなのは知るうちに入るのか入らないのか。この点がよくわかりません。
例えば、SubView.xamlを新しく表示する場合、SubViewという確定している名前がコードに書かれているだけなら、それで知っていることになるのか?ということです。
これについては、パターン2の、Commandパラメータで考えます。

コード

開発環境

.NET Core 3.1 + ReactiveProperty で構築します。

パターン 1 Viewのコードビハインド

アプリを起動した時の最初のViewは、App.xamlのStartupUriで指定します。
それ以降のViewを、Viewのコードビハインドから表示します。

ファイルの構成

Class
Model MainManager
ViewModel MainViewModel
View MainView

Newする順序は、

  1. ViewからViewModelをNew
  2. NewされたViewModelから、ModelをNewします。

V → VM → M の順序です。

プロジェクトの構成

スタート時のウインドウ

App.xamlの、StartupUriに、MainView.xamlを指定しました。

<Application
    x:Class="ShowView1.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ShowView1"
    StartupUri="View/MainView.xaml">

MainViewのコードビハインド

MainViewModelをNewしています。
DataContextに、vmをセットします。ここではなく、XAMLでVMを指定もできます。
ただし、ViewのコードビハインドでVMのメソッドを呼び出すことがあるなら、ここで指定しておくと便利だと思います。

  public partial class MainView : Window
    {
        MainViewModel vm = new MainViewModel();
        public MainView()
        {
            InitializeComponent();
            this.DataContext = vm;
        }

ViewModel

ModelをNewして保持します

    class MainViewModel : INotifyPropertyChanged, IDisposable
    {
        public MainManager Model { get; }

        public MainViewModel()
        {
            Model = new MainManager();

ボタンクリックで新しいViewを表示

ボタンをクリックして、別のViewを表示するには、

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var cls = new MainView();
            cls.Show();
        }

Viewのコードビハインドで良いはずです。
ここでは、2つめのMainViewを表示しています。

Viewと他のViewとの関係

ここでは、ボタンクリックで、MainViewをさらに表示していますが、他のViewを表示するのも同じです。

SubViewがあるとすれば、

  • MainViewのボタンクリック
  • SubViewを表示

になります。
これだと、MainViewは、SubViewを知っていることになります。SubViewという名前を知っているし、参照もしています。MVVMとして、それで良いのかどうかが分かりません。

View1が、View2を、表示する処理(閉じる処理も?)を、View1で行って良いとすると、また、例えば、View2のDialogResultを、View1で受け取り、ViewModel, ViewModel経由でModelで処理をする程度のこともOKかもしれません。

Viewのコードビハインドの是非

Viewには、できるだけコードを書かないでおこうという考え方もあるのですが、私は次の条件の時は、Viewにコードを書くと良いと考えます。

  • Viewに関わる事
  • Viewのコードで処理した方が、ViewModelやModelで処理するより容易

XAMLに書くのも、Viewにコードビハインドで書くのも同じViewでの処理です。単に、言語がXAMLなのかC#なのか程度の違いです。
https://stackoverflow.com/a/37294259/9924249

XAMLだけで処理をしようとして、複雑なコードになるのは本末転倒な気がします。また、UIのテストが行いやすくなるからXAMLだけで処理をしようとするのも、同様に本末転倒に思います。

同様の例

Handle it in the view
https://stackoverflow.com/a/5829704/9924249

private void AddCustomerView_Click(object sender, RoutedEventArgs e)
{
    AddCustomerView view = new AddCustomerView(data);
    view.Show();
}

となっています。ここでは、ViewをNewする時に、引数としてdataを渡していますが、このdataはモデルのデータであるはずです。それを、Viewから渡すことは、MVVMらしいでしょうか? これも分からない点です。

疑問点まとめ

  • Viewが他のViewを知っているのはMVVMとしてそれでよいのか?
  • ViewからViewを表示する場合、引数(Modelのデータ)をどうやって渡すのか?

単純な処理なようで、疑問も増えました。このパターンを基本として、他のパターンと比べていきましょう。

記事予定