LoginSignup
2
2

More than 3 years have passed since last update.

MVVM List Detail パターン

Last updated at Posted at 2020-09-30

コード

ListDetail

今まで、ListとDetailをその都度、ああでもない、こうでもないと試行錯誤して書いていたので、パターンにまとめてみました。

開発環境

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

ListViewModelとDetailViewModel

image.png

ListViewModel.cs
this.InfoList = this.Model.InfoList.ToReadOnlyReactiveCollection(x => new DetailViewModel(x)).AddTo(Disposable);

で、DataGridのそれぞれの行に対応する、それぞれのDetailViewModelを作成しています。
MVVMを学び始めた当初、DataGridやListViewではこうする方法があることが分からなかったですね。

Show

ShowボタンのXAMLは次です。
Commandは、DataGridのDataContextつまり、WindowのDataContext(=ListViewModel)のButtonShowを呼び出します。
パラメータは、CommandParameter="{Binding}"によって、この行のDetailViewModelになります。

ListView.xaml
<Button Command="{Binding DataContext.ButtonShow, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding}">Show</Button>
ListViewModel.cs
public ReactiveCommand<DetailViewModel> ButtonShow { get; } = new ReactiveCommand<DetailViewModel>();

その行のDetailViewModel自体をパラメータとしてうけとり(ここではxが、パラメータ)

ListViewModel.cs
ButtonShow.Subscribe(x => ShowDetail(x)).AddTo(Disposable);

private void ShowDetail(DetailViewModel infoVM)
{
    Model.ShowDetail(infoVM.Model);
}

DetailViewModelが保持しているModel(infoVM.Model)を、ListManagerに渡して、Detailを表示します。

従って、DataGridの行でもDetailでも、Modelは同じものを参照しますが、VMはそれぞれ異なります。

image.png

DataGridの行で利用している、同じDetailViewModelを利用して、Detailを表示したいなら、

ListViewModel.cs
private void ShowDetail(DetailViewModel infoVM)

の段階で、infoVMを利用して、ListViewModel.csから、DetailViewを表示してもいいかもしれません。そうすると、DataGridの行と新しく表示したDetailViewでのDataContextは、同じDetailViewModelを利用することになるでしょう。

Remove

Showとほぼ同じです。

newの順序

Model → ViewModel  → View の順序でnewしています。

Main.cs
var list = new ListManager();
ViewController.ShowListView(list);

ViewとViewModelのnewは、ViewControllerpublic staticのクラスで処理することにしました。

ViewController.cs
public static void ShowListView(ListManager model)
{
    var viewModel = new ListViewModel(model);
    var view = new ListView(viewModel);
    view.Show();
}

View  → ViewModel → Model の方法もありますが、ここではModelが先です。
バックグラウンドでModelが動いていて、ある条件でViewを表示するなら、Modelを先にせざる得ないでしょうし、Modelが無くてVとVMだけあることはMVVMでは考えにくいので、Modelを先にnewするのがいいのではという考えです。

Disposeのタイミング

ReactivePropertyで、よく分からないのはDisposeを呼び出すタイミングです。

DataGridのViewModel

image.png

DataGridのClear・Removeは、ListManager.csObservableCollection<Info> InfoListを、ClearしたりRemoveしています。
すると、ListViewModelの、ReadOnlyReactiveCollection<DetailViewModel> InfoListが変更されます。このコードでは、すぐに、DetailViewModelDisposeされたので、ここでは明示的にはDetailViewModelDisposeを呼び出していません。(これで良いのか?がよくわかりません。)

DetailのViewModel

image.png

DetailViewCloseした時は、Disposeされるのかどうかが分からないので、明示的にDiposeしています。

DetailViewModel.cs
private void Close()
{
    CloseAction();
    Dispose();
}

感想

処理の流れがはっきりせず、デバッグしつつ追わないと、コードを見るだけでは分かりませんね。この点が、MVVMの難しいところだと思います。
ただ、パターン化しておくと、処理の流れの見当が少しつきやすくなりますね。

MVVMの記事

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2