You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

取消窗口关闭后再次尝试关闭引发InvalidOperationException异常

WPF窗口关闭时的InvalidOperationException问题分析与解决

问题场景

定义了如下关闭接口:

public interface ICloseable
{
    event EventHandler Closing;
    event EventHandler Closed;
    void Close();
}

ViewModel实现该接口:

public class TestViewModel : ICloseable
{
    public event EventHandler Closing;
    public event EventHandler Closed;

    public void Close()
    {
        RaiseEvent(Closing);
        
        // do some cleaning...
        
        RaiseEvent(Closed);
    }

    protected virtual void RaiseEvent(EventHandler handler)
    {
        handler?.Invoke(this, null);
    }
}

View中绑定ViewModel并处理关闭逻辑:

public partial class TestView : Window
{
    private bool _contextClosed = false; // true if DataContext has been closed

    public TestView()
    {
        InitializeComponent();

        var context = new TestViewModel();

        Closing += TestView_Closing;
        DataContext = context;

        context.Closed += TestViewModel_Closed;
    }

    private void TestView_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        var context = DataContext as ICloseable;
        if (!_contextClosed)
        {
            e.Cancel = true;
            context.Close();
            return;
        }
    }

    private void TestViewModel_Closed(object sender, EventArgs e)
    {
        _contextClosed = true;
        Application.Current.Dispatcher.Invoke(() => Close());
    }

    private void TestButton_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }
}

点击窗口标题栏的“X”或TestButton触发关闭时,会在TestViewModel_Closed的Application.Current.Dispatcher.Invoke(() => Close());行抛出异常:

System.InvalidOperationException: 'Cannot set Visibility to Visible or call Show, ShowDialog, Close, or WindowInteropHelper.EnsureHandle while a Window is closing.'

问题原因

虽然在TestView_Closing中设置了e.Cancel = true,但当前的Closing事件处理流程还未完全结束,窗口仍处于“正在关闭”的状态周期内。此时同步调用context.Close()会立即触发Closed事件,进而同步执行Dispatcher.Invoke里的Close()——这相当于在窗口Closing的过程中再次调用Close方法,WPF不允许这种操作,因此抛出异常。

简单来说:第一次Close触发的Closing事件还在处理中,就立刻发起了第二次Close调用,窗口状态未完成重置,导致冲突。

解决方案

核心思路是让第二次Close调用延迟到当前Closing事件处理完成之后执行,可通过以下两种方式实现:

方式1:异步触发ViewModel的Closed事件

修改TestViewModel的Close方法,用Dispatcher.BeginInvoke异步触发Closed事件,避免同步执行导致的状态冲突:

public void Close()
{
    RaiseEvent(Closing);
    
    // do some cleaning...
    
    // 异步触发Closed事件,等待当前Closing流程结束后再执行后续逻辑
    Application.Current.Dispatcher.BeginInvoke(() => RaiseEvent(Closed));
}

方式2:异步执行View的Close调用

修改TestViewModel_Closed方法,将Dispatcher.Invoke改为Dispatcher.BeginInvoke,把Close操作放到Dispatcher队列的后续执行:

private void TestViewModel_Closed(object sender, EventArgs e)
{
    _contextClosed = true;
    // 延迟执行Close,让当前Closing事件处理完成后再执行
    Application.Current.Dispatcher.BeginInvoke(() => Close());
}

两者的本质都是让第二次Close操作避开当前的Closing事件处理周期,等窗口状态重置为正常后再执行,即可解决异常。

内容的提问来源于stack exchange,提问作者AntikM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 05:42:31