redwarrior’s diary

C#, ASP.NET, WEB, G*などの雑多な情報

WPFアプリのWindowの閉じる・開くを、MVVMフレームワークを使用して実装する

WPFアプリケーションで、自作のボタンを押して画面を閉じて、新しい画面を表示する処理をMVVMフレームワークを使用して実装する方法を調べました。

MVVMフレームワークは、Prismを使用します。

自作のボタンを押して画面を閉じる

自作のボタンを押して画面を閉じるやり方です。これは、以下で紹介されているCommandParameter属性を使用する方法にしました。WindowクラスをViewModelが参照するので、若干気になる部分ではありますが、引数なので良しとし、コードビハインドを使用しないという方向で決めました。

c# - Close Window from ViewModel - Stack Overflow

Windowにx:Nameで名前を定義するのではなく、RelativeSourceを使用して自分の親Windowを取得するようにします。

XAML

<Button Width="120" Height="30"
        Command="{Binding OpenCommand}"
        CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
    Open New Window
</Button>

ViewModel

public DelegateCommand<Window> OpenCommand { get; set; }

public MainWindowViewModel()
{
    OpenCommand = new DelegateCommand<Window>(ExecuteOpen);
}

private void ExecuteOpen(Window window)
{
    window?.Close();
}

新しい画面を表示する

続いて新しい画面を表示する方法です。新しい画面を表示しないとアプリケーションが終了してしまいますからね。

実は、(呼び出し元の)ViewModelから(呼び出し先の)Windowをnewして、Showメソッドを呼び出すだけで画面表示そのものは出来てしまいます。PrismがViewModelの紐づけや、DIを行ってくれます。
しかし、メソッド内で特定のView(Window)をnewして、Showメソッドまで呼び出しているので、上とは違って、このままではMVVMとして良くありませんし、単体テストも難しいです。

以下のサイトを参考にして、解決しました。

c# - Open a new Window in MVVM - Stack Overflow

Opening new window in MVVM WPF

WindowをnewしてShowメソッドを呼び出す処理をメソッドにし、画面表示用のクラスを用意して、そこにメソッドを移動します。
ViewModelからは、画面表示用クラスのメソッドを呼び出します。画面表示用のクラスは、DIで取得します。

画面表示用クラス

public class RouteService : IRouteService
{
    private readonly IContainerExtension _container;

    public RouteService(IContainerExtension container)
    {
        _container = container;
    }

    public void ShowBrandNewWindow(string username)
    {
        var brandNewWindow = _container.Resolve<BrandNewWindow>();
        brandNewWindow.Show();
    }
}

ViewModel

public DelegateCommand<Window> OpenCommand { get; set; }

public MainWindowViewModel(IRouteService routeService)
{
    _routeService = routeService;

    OpenCommand = new DelegateCommand<Window>(ExecuteOpen);
}

private void ExecuteOpen(Window window)
{
    _routeService.ShowBrandNewWindow(Username);

    window?.Close();
}

開発環境:

  • .NET Core 3.1

  • Prism 7.2.0.1422

おまけ

今回と一つ前の内容をサンプルプロジェクトとして、GitHubリポジトリに追加したので、全体像を見たい方はどうぞ。

github.com

以上。