7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MVVMに適した、new Windowの方法 まとめ

Last updated at Posted at 2024-12-12

MVVMに適した、Windowをnewする方法は、大きく分けて、次の3種類になるでしょう。

  1. Viewのコードビハインド。
  2. DIの、Serviceで処理する。
  3. フレームワークで処理する。

1と2は、次のコードを参照して下さい。

image.png

1. Viewのコードビハインド。

var view = new MainView(viewModel);

ただし、これは、Viewのコードビハインドでのみ利用します。
MainViewを、 Model, ViewModelで参照するのは、MVVMに適しません。

これを解消するには、例えば、Model, ViewModelから、Viewのメソッド(new Windowする)を呼び出すAction(実体は、delegateと同じ) を利用します。

留意点:Actionは、メモリーリークの原因になりやすいので、利用しなくなった時はnullにします。

MainViewModel
public Action? ShowWindowAction { get; set; }
    
public void ShowWindow()
{
    ShowWindowAction?.Invoke();
}
MainView
public MainView(MainViewModel viewModel)
{
    InitializeComponent();
    vm = viewModel;
    DataContext = vm;
    vm.ShowWindowAction ??= new Action(ShowWindow);
}

/// <summary>
/// Actionから呼び出される。
/// </summary>
public void ShowWindow()
{
    var model = new MainModel();
    var viewModel = new MainViewModel(model);
    var view = new MainView(viewModel);
    view.Show();
}

2. DIの、Serviceで処理する。

WindowServiceを、Dependency Injection(DI・依存性の注入・依存オブジェクト注入)で作成します。
そして、次のどちらかを利用します。

  • public void ShowMainView()のように、メソッド内で特定のウインドウを表示する。
  • ジェネリックメソッド

サービス化の方法は、次と同じです。
MVVMに適した方法で、DIでMessageBoxをサービスにし、テストもする。

特定のウインドウを表示するメソッド

public class WindowService : IWindowService
{
    /// <summary>
    /// Model, ViewModel, Viewの、いずれのレイヤーから呼び出し可能。
    /// </summary>
    public void ShowMainView()
    {
        var model = new MainModel();
        var viewModel = new MainViewModel(model);
        var view = new MainView(viewModel);
        view.Show();
    }

ジェネリックメソッド

ジェネリックメソッドの場合は、どのWindowを表示するかを、なんらかの形で呼び出す側が指定する必要があります。

例1
public void ShowWindow<T>(object dataContext) where T : Window, new()
{
    T window = new T();
    window.DataContext = dataContext;
    window.Show();
}
var model = new SubModel();
var viewModel = new SubViewModel(model);
//ここに、SubViewへの参照があるので、MVVMに適した形にするには、Viewのレイヤーで処理する。
_windowService.ShowWindow<SubView>(viewModel);
  • この例では、サービスを呼び出す時に、SubViewを指定しているので、Viewのコードビハインドで実行しないと、MVVMに適しません。
    ViewModel, Model上で呼び出して、MVVMを満たすようにするには、Windowを文字列で指定し、サービス側で、指定したWindowの型を生成します。例えば、Prismでは、文字列で指定します。
  • where T : new() 制約で、「パラメーターなしのパブリック コンストラクター」が必要になります。
    コンパイラ エラー CS0310

参考:

例2

MVVMでViewModelから別ウィンドウ表示をするサンプル~シンプルなTODOリスト~

3. フレームワークで処理する。

新規ウインドウを表示する機能をもつフレームワークを利用します。

Prism

PrismのIDialogServiceは、Viewの名前の文字列で、どのWindowを表示するか指定できます。

App.xaml.cs
containerRegistry.RegisterDialog<NotificationDialog, NotificationDialogViewModel>();

で登録

MainWindowViewModel.cs
_dialogService.ShowDialog("NotificationDialog", new DialogParameters($"message={message}")

"NotificationDialog" の文字列で指定

ちなみに、Prismは、Ver 9からライセンスが変更(有料版が登場)になっていますね。

フレームワーク「KAMISHIBAI」

WPF用Generic Host対応MVVMフレームワーク「KAMISHIBAI」

var builder = KamishibaiApplication<App, MainWindow>.CreateBuilder();
builder.Services.AddPresentation<SubView, SubViewModel>();

で、ViewとViewModelを登録

_presentationService.OpenWindowAsync<SubViewModel>();

で、表示

どれが良いか?

いずれもMVVMの要件は満たすので、プロジェクトに応じてどれを採用するか判断すれば良いでしょう。

「1. Viewのコードビハインド」

  • ある程度の規模のアプリで、多数の種類のウインドウがあるなら、コードを書くのが辛くなります。
    例えば、Actionを使うと、処理の流れが複雑になり、デバッグ時にコードを追うのが大変です。

「2. DIの、Serviceで処理する。」

  • DIの理解を必要としますが、DIのメリットは大きいです。

「3. フレームワークで処理する。」

  • 新規ウインドウ表示以外にも便利な機能が多く、それらのメリットも大きいです。
  • 保守が必要なアプリの場合、フレームワークに変更があれば、それに対応する必要があります。破壊的変更があると、変更に相当時間がかかります。
7
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
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?