Show() で開いたモデルレスウィンドウには DialogResult や IsCancel が効かないため、自前でクローズ処理を用意する必要があります。ここでは MVVM 前提でよく使う 2 つのパターンを、具体的なクラス例と実装ステップ付きでまとめます。
1. IMessenger でクローズを通知するパターン
CommunityToolkit.Mvvm の IMessenger を使い、ViewModel から View へ「閉じてください」というメッセージを送ります。コードビハインドは最小限で済み、依存は Toolkit にまとまります。
例えばこのようなクラスを作成する
-
Messages/CloseWindowMessage.csを作り、共通で参照できる名前空間に置く。
// Messages/CloseWindowMessage.cs
namespace DataGridSample.Messages;
public sealed record CloseWindowMessage;
実装ステップ
- 上記のクローズ用メッセージを用意する。
- ViewModel が「閉じたい」タイミングでメッセージを送信する。
- Window 側がそのメッセージを購読し、受信したら
Close()を呼ぶ。
サンプルコード
- ViewModel 側(ボタンの Command や処理完了時に送信)
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using DataGridSample.Messages;
public partial class SampleViewModel
{
[RelayCommand]
private void CloseCommand()
{
// 「このウィンドウを閉じて」と依頼
WeakReferenceMessenger.Default.Send(new CloseWindowMessage());
}
}
- Window 側(Loaded/Unloaded で購読・解除)
using CommunityToolkit.Mvvm.Messaging;
using DataGridSample.Messages;
public partial class SampleWindow : Window
{
public SampleWindow()
{
InitializeComponent();
Loaded += (_, __) => WeakReferenceMessenger.Default.Register<CloseWindowMessage>(this, OnCloseRequested);
Unloaded += (_, __) => WeakReferenceMessenger.Default.UnregisterAll(this);
}
private void OnCloseRequested(object recipient, CloseWindowMessage message)
{
Close(); // メッセージを受け取ったら閉じる
}
}
- XAML 例(ボタンから閉じるコマンドを実行)
<Button Content="閉じる" Command="{Binding CloseCommand}" />
- 使いどころ
- Toolkit を既に使っている/イベントを増やしたくない
- 複数の購読先に同じメッセージを届けたい
- コードビハインドを薄く保ちたい
2. RequestClose イベントで閉じるパターン
ViewModel で「閉じたい」ときにイベントを発火し、Window 側でそれを受けて Close() します。依存を増やしたくない場合に向きます。
例えばこのように書く
- ViewModel にイベントを追加(既存ファイルに追記でOK)。
public event EventHandler? RequestClose;
public void Close()
{
RequestClose?.Invoke(this, EventArgs.Empty);
}
- Window を作る側(例: WindowService)で購読して閉じる。
public void ShowWindow()
{
var window = new SampleWindow { DataContext = viewModel, Owner = Application.Current.MainWindow };
void Handler(object? sender, EventArgs e) => window.Close();
viewModel.RequestClose += Handler;
window.Show(); // ShowDialog ではなく Show(モデルレス)
viewModel.RequestClose -= Handler; // 後始末を忘れずに
}
- Window を作る側(例: WindowService)で購読して閉じる。(DIを使用)
public void ShowWindow()
{
var win = _insertViewFactory(); // DIで View + ViewModel を生成
win.Owner = Application.Current.MainWindow;
if (win.DataContext is InsertViewModel vm)
{
void Handler(object? sender, EventArgs e)
{
vm.RequestClose -= Handler; // 購読解除(リーク防止)
win.Close();
}
vm.RequestClose += Handler;
}
win.ShowDialog(); // 入力完了を待つ
}
}
- XAML 例(ボタンで閉じる)
<Button Content="閉じる" Command="{Binding CloseCommand}" />
- コマンド内で
RequestClose?.Invoke(...)を呼ぶだけで閉じられます。
実装ステップ
- ViewModel に
RequestCloseイベントを用意し、閉じたいときに発火するメソッド(または Command)を用意。 - Window を生成する箇所でイベントを購読し、受信時に
Close()を呼ぶ。 -
Show()で開いた後、購読解除する。
- 使いどころ
- Toolkit への依存を避けたい
- シンプルな 1 対 1 の閉じる通知で十分
- 既存の
RequestClose実装をDialogResultからClose()呼び出しに置き換えるだけで対応したい
どちらを選ぶか
- Toolkit を使っていて、汎用的に通知したい → IMessenger
- 依存を増やさず単純な通知だけでよい → RequestClose イベント
どちらのパターンでも、Show() で開いたウィンドウは自動で閉じないため、クローズ通知を自前で流すのがポイントです。IsCancel や DialogResult は ShowDialog() 専用であることを覚えておいてください。