0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【WPF / MVVM Toolkit】ViewModel から Window を閉じる方法まとめ(イベント / Messenger)

Posted at

Show() で開いたモデルレスウィンドウには DialogResultIsCancel が効かないため、自前でクローズ処理を用意する必要があります。ここでは MVVM 前提でよく使う 2 つのパターンを、具体的なクラス例と実装ステップ付きでまとめます。


1. IMessenger でクローズを通知するパターン

CommunityToolkit.Mvvm の IMessenger を使い、ViewModel から View へ「閉じてください」というメッセージを送ります。コードビハインドは最小限で済み、依存は Toolkit にまとまります。

例えばこのようなクラスを作成する

  • Messages/CloseWindowMessage.cs を作り、共通で参照できる名前空間に置く。
// Messages/CloseWindowMessage.cs
namespace DataGridSample.Messages;
public sealed record CloseWindowMessage;

実装ステップ

  1. 上記のクローズ用メッセージを用意する。
  2. ViewModel が「閉じたい」タイミングでメッセージを送信する。
  3. 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(...) を呼ぶだけで閉じられます。

実装ステップ

  1. ViewModel に RequestClose イベントを用意し、閉じたいときに発火するメソッド(または Command)を用意。
  2. Window を生成する箇所でイベントを購読し、受信時に Close() を呼ぶ。
  3. Show() で開いた後、購読解除する。
  • 使いどころ
    • Toolkit への依存を避けたい
    • シンプルな 1 対 1 の閉じる通知で十分
    • 既存の RequestClose 実装を DialogResult から Close() 呼び出しに置き換えるだけで対応したい

どちらを選ぶか

  • Toolkit を使っていて、汎用的に通知したい → IMessenger
  • 依存を増やさず単純な通知だけでよい → RequestClose イベント

どちらのパターンでも、Show() で開いたウィンドウは自動で閉じないため、クローズ通知を自前で流すのがポイントです。IsCancelDialogResultShowDialog() 専用であることを覚えておいてください。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?