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

C# WinForm等待窗体关闭时终止任务的实现方案咨询

解决WinForm Excel处理任务的终止问题

需求说明

开发WinForm应用处理大型Excel文件,执行任务时显示等待窗体。要求:点击等待窗体关闭按钮时弹出确认框,确认后终止当前任务但不退出主程序,后续可重新发起处理。

关键实现步骤

1. 改造等待窗体(PleaseWait)

添加公共属性标记用户是否确认终止任务,保留原有的关闭确认逻辑:

public partial class PleaseWait : Form
{
    public bool IsCancellationConfirmed { get; private set; } = false;

    private void PleaseWait_FormClosing(object sender, FormClosingEventArgs e)
    {
        var result = MessageBox.Show("确定要终止任务吗?", "确认终止", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
        if (result == DialogResult.No)
        {
            e.Cancel = true;
            IsCancellationConfirmed = false;
        }
        else
        {
            IsCancellationConfirmed = true;
            e.Cancel = false;
        }
    }
}

2. 在主窗体(Home.cs)中集成CancellationToken

使用CancellationTokenSource控制任务取消,监听等待窗体关闭事件,在用户确认终止时触发取消信号:

private CancellationTokenSource _cts;

private async void btnClick()
{
    try
    {
        _cts = new CancellationTokenSource();
        var pleaseWait = new PleaseWait();
        
        // 监听等待窗体关闭事件,确认终止则取消任务
        pleaseWait.FormClosed += (s, e) =>
        {
            if (pleaseWait.IsCancellationConfirmed)
            {
                _cts.Cancel();
            }
        };
        
        pleaseWait.Show();

        // 传入CancellationToken执行异步任务
        await Task.Run(() => ProcessBookstoreExcel(_cts.Token), _cts.Token);

        pleaseWait.Close();
        MessageBox.Show("处理完成!");
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show("任务已终止");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"处理出错:{ex.Message}");
    }
    finally
    {
        _cts?.Dispose();
        // 确保等待窗体关闭
        foreach (Form form in Application.OpenForms)
        {
            if (form is PleaseWait)
            {
                form.Close();
            }
        }
    }
}

3. 修改Excel处理方法,加入取消检查

在ProcessBookstoreExcel中接受CancellationToken,在耗时循环步骤中检查取消信号,及时终止任务:

private void ProcessBookstoreExcel(CancellationToken cancellationToken)
{
    string path = TxtBox.Text;
    ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
    var records = new List<BookRecord>();

    using (var package = new ExcelPackage(new FileInfo(path)))
    {
        ExcelWorksheet worksheet = package.Workbook.Worksheets[0];
        int rowCount = worksheet.Dimension.Rows;

        for (int row = 2; row <= rowCount; row++)
        {
            // 每次循环检查是否取消,若已取消则抛出异常终止任务
            cancellationToken.ThrowIfCancellationRequested();
            
            records.Add(new BookRecord
            {
                LineNumber = row,
                ISBN = worksheet.Cells[row, 1].Text,
                Title = worksheet.Cells[row, 2].Text,
                Stock = int.Parse(worksheet.Cells[row, 3].Text),
                Price = decimal.Parse(worksheet.Cells[row, 4].Text),
                PublisherID = int.Parse(worksheet.Cells[row, 5].Text),
                PublisherName = worksheet.Cells[row, 6].Text
            });
        }
    }

    var discrepancies = new List<Discrepancy>();
    var groupedRecords = new Dictionary<string, Dictionary<string, (decimal Price, int Stock)>>();

    foreach (var record in records)
    {
        // 处理记录时也检查取消信号
        cancellationToken.ThrowIfCancellationRequested();
        
        var publisherKey = record.PublisherID.ToString();
        var isbnKey = record.ISBN;

        if (!groupedRecords.ContainsKey(publisherKey))
        {
            groupedRecords[publisherKey] = new Dictionary<string, (decimal Price, int Stock)>();
        }

        if (!groupedRecords[publisherKey].ContainsKey(isbnKey))
        {
            groupedRecords[publisherKey][isbnKey] = (record.Price, record.Stock);
        }
        else
        {
            var existing = groupedRecords[publisherKey][isbnKey];
            if (existing.Price != record.Price || existing.Stock != record.Stock)
            {
                discrepancies.Add(new Discrepancy
                {
                    LineNumber = record.LineNumber,
                    Publisher = record.PublisherName,
                    Title = record.Title
                });
            }
        }
    }

    // 取消时不执行写入操作
    cancellationToken.ThrowIfCancellationRequested();
    WriteDiscrepancies(discrepancies);
    MessageBox.Show("处理完成,请查看输出文件中的差异记录。");
}

4. 核心注意点

  • 必须在耗时循环中插入取消检查,确保任务能及时响应终止请求;
  • cancellationToken.ThrowIfCancellationRequested()会抛出OperationCanceledException,上层捕获后可给出终止提示;
  • 任务取消后finally块自动清理资源并关闭等待窗体,避免泄漏;
  • 取消后不会执行写入操作,避免生成不完整的输出文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:50:02