WPFアプリのWindowの閉じる・開くを、MVVMフレームワークを使用して実装する
WPFアプリケーションで、自作のボタンを押して画面を閉じて、新しい画面を表示する処理をMVVMフレームワークを使用して実装する方法を調べました。
自作のボタンを押して画面を閉じる
自作のボタンを押して画面を閉じるやり方です。これは、以下で紹介されているCommandParameter属性を使用する方法にしました。WindowクラスをViewModelが参照するので、若干気になる部分ではありますが、引数なので良しとし、コードビハインドを使用しないという方向で決めました。
c# - Close Window from ViewModel - Stack Overflow
Windowにx:Nameで名前を定義するのではなく、RelativeSourceを使用して自分の親Windowを取得するようにします。
<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リポジトリに追加したので、全体像を見たい方はどうぞ。
以上。