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

任务完成后如何关闭模态窗口?WPF实现问题咨询

问题分析与解决方案

咱们先逐个拆解你遇到的三个问题,然后给出符合WPF异步规范的修复代码:


问题1:任务完成后ProgressWindow无法自动关闭

原因有两个:

  • 你的Go方法是async void,用Task.Run(() => Go())时,这个Task会在Go执行到第一个await(也就是await Task.Delay(500))的时候就标记为完成了——因为async void方法无法被跟踪后续执行流程,Task.Run认为方法已经跑完了,所以ContinueWith会提前触发,但此时MessageBox.Show("Completed!")还没执行。
  • 就算ContinueWith触发了,w.Close()是在后台线程调用的,WPF严格禁止跨线程操作UI控件,这个Close操作要么被静默忽略,要么抛出异常被吞掉,导致窗口关不掉。

问题2:手动关闭窗口时抛出System.InvalidOperationException

当你手动点击红叉关闭窗口后,w已经被UI线程释放,但后台线程的ContinueWith还试图调用w.Close()——而且ContinueWith默认在后台线程执行,跨线程访问UI控件直接触发WPF的线程安全检查,抛出异常。另外,await task时,窗口已经被关闭,上下文切换也会引发问题。

问题3:ContinueWith在Go方法结束前执行

核心原因还是async void!async void是为事件处理程序设计的,它没有返回Task,所以Task.Run(() => Go())创建的Task只能等待到Go中的第一个await点,之后就认为任务完成了,直接触发ContinueWith,但此时Go里的MessageBox.Show还在后台线程排队执行,所以看起来ContinueWith提前跑了。


修复后的完整代码

我把代码改成符合WPF异步最佳实践的写法,解决所有问题:

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        async void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var progressWindow = new ProgressWindow();
            // 用取消令牌处理手动关闭窗口的场景
            using var cts = new CancellationTokenSource();

            // 窗口关闭时取消后台任务
            progressWindow.Closed += (s, args) => cts.Cancel();

            try
            {
                // 启动后台任务,传递取消令牌
                var task = Task.Run(async () =>
                {
                    await Go(cts.Token);
                }, cts.Token);

                // 显示模态进度窗口
                progressWindow.ShowDialog();

                // 等待后台任务完成(如果没被取消的话)
                await task;

                // 回到UI线程,关闭窗口(如果还没关)
                if (progressWindow.IsVisible)
                {
                    progressWindow.Close();
                }

                // 在UI线程弹出完成提示
                MessageBox.Show("Completed!");
            }
            catch (OperationCanceledException)
            {
                // 手动关闭窗口时,任务被取消,这里可以做清理逻辑
                Console.WriteLine("Progress window closed manually.");
            }
        }

        // 把Go改成async Task,让Task.Run能完整跟踪异步流程
        async protected Task Go(CancellationToken token)
        {
            // 模拟耗时操作,支持取消
            await Task.Delay(500, token);
        }
    }
}

关键修改点说明

  1. 把Go改为async Task:这样Task.Run可以完整等待Go的整个异步流程,不会在第一个await点就提前结束,解决了ContinueWith提前执行的问题。
  2. 使用CancellationTokenSource:监听窗口的Closed事件,手动关闭窗口时取消后台任务,避免资源浪费,同时捕获OperationCanceledException处理取消场景,不会抛出异常。
  3. UI操作回到UI线程:await在WPF中会自动回到UI线程上下文,所以任务完成后关闭窗口、弹出提示都在UI线程执行,不会有跨线程访问的问题。
  4. 替换ContinueWith为await:await比ContinueWith更易读,且自动处理上下文切换,避免了手动线程调度的麻烦。

这样修改后,你的三个问题都能解决:任务完成后进度窗口自动关闭,手动关闭窗口不会抛出异常,后续逻辑也会在Go完全结束后执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:47:36