Остановить выполнение приложения при закрытии формы C#

Я пишу приложение winform, которое читает большой файл Excel, выполняет некоторую обработку и записывает выходные данные в какой-то другой файл Excel. Пока основная форма выполняет свою задачу, мое приложение отображает форму «Пожалуйста, подождите». Я хочу реализовать функциональность, когда, если пользователь нажимает на крестик в форме, пожалуйста, подождите, ему должно быть предложено, действительно ли он хочет выйти, если он нажимает «Да», тогда приложение должно прекратить выполнение. Я не хочу использовать application.Exit(0). Я просто хочу остановить казнь. Основная форма по-прежнему должна отображаться, и если пользователь снова начнет обрабатывать файл, это должно произойти с самого начала.

Вот мой код -

Home.cs

private async void btnClick()
{
try{
    PleaseWait pleasewait = new PleaseWait();
    pleasewait.Show();

    // Perform your long-running task asynchronously
    await Task.Run(() => ProcessBookstoreExcel());

    pleasewait.Close();
    MessageBox.Show("Processing Complete!");
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}


private void ProcessBookstoreExcel()
{
    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++) // Skip header row
        {
            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)
    {
        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
                });
            }
        }
    }

    WriteDiscrepancies(discrepancies);
    MessageBox.Show("Processing complete. Check the output file for discrepancies.");
}

private void WriteDiscrepancies(List<Discrepancy> discrepancies)
{
    var outputPath = Path.Combine(Path.GetDirectoryName(filePath), "discrepancies.xlsx");

    using (var package = new ExcelPackage())
    {
        var worksheet = package.Workbook.Worksheets.Add("Discrepancies");
        worksheet.Cells[1, 1].Value = "LineNumber";
        worksheet.Cells[1, 2].Value = "Publisher";
        worksheet.Cells[1, 3].Value = "Title";

        for (int i = 0; i < discrepancies.Count; i++)
        {
            worksheet.Cells[i + 2, 1].Value = discrepancies[i].LineNumber;
            worksheet.Cells[i + 2, 2].Value = discrepancies[i].Publisher;
            worksheet.Cells[i + 2, 3].Value = discrepancies[i].Title;
        }

        package.SaveAs(new FileInfo(outputPath));
    }
}



Пожалуйста, подождите.cs

private void PleaseWait_FormClosing(object sender, FormClosingEventArgs e)
{
    var x = MessageBox.Show("Are you sure you want to really exit ? ", 
                             "Exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

    if (x == DialogResult.No) 
    {
       e.Cancel = true;
    }
    else
    {
      e.Cancel = false;
    }
}

Я не понимаю, как правильно захватить e.Cancel в Home.cs.

Редактировать :

Я читал о CancellationToken, но не уверен, как его использовать.

if (cancellationToken.IsCancellationRequested) в ProcessBookStoreExcel()

Я думаю, мне следует использовать его, чтобы я мог выдать сообщение/исключение о том, что выполнение было отменено. Я не знаю, нужно это или нет. Пожалуйста, направляйте.

Вы можете запустить задачу асинхронно и реализовать функцию кнопки отмены для своей задачи в том же приложении. Или это должно быть другое приложение, запрашивающее у пользователя отмену?

tom2051 31.07.2024 09:39

@shingo Я проверил CancellationToken, но не был уверен, как буду использовать if (cancellationToken.IsCancellationRequested) в ProcessBookStoreExcel().

Anonymous 31.07.2024 09:39

@tom2051 Tom2051 Я хочу, чтобы, если пользователь закроет форму pleaseWait, он должен остановить выполнение после подтверждения. Я просто застрял в том, как обрабатывать e.Cancel с помощью async и жду

Anonymous 31.07.2024 09:42

Вызовите cancellationToken.ThrowIfCancellationRequested() в каждом цикле и добавьте catch(OperationCancelledException) к месту вызова. Таким образом, вы узнаете, была ли задача отменена или нет.

JonasH 31.07.2024 09:43

Вам необходимо проверять IsCancellationRequested перед каждым (важным) шагом.

shingo 31.07.2024 09:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В общем, с вашим кодом есть некоторые проблемы, однако я остановлюсь на вашем вопросе.

Чтобы решить проблему, вам необходимо реализовать логику удаления задачи, если так решит пользователь, и для этого вы можете использовать CancellationToken.

Итак, вы объявляете CancellationTokenSource в классе Home.cs, который вы будете использовать в случае, если форма PleaseWait закроется до завершения задачи, чтобы отменить ее.

Имейте в виду, что я использовал метод ThrowIfCancellationRequested, который генерирует исключение, хотя вы также можете использовать свойство IsCancellationRequested.

Home.cs

CancellationTokenSource cts;

private async Task btnClick()
{
    try
    {
        cts = new CancellationTokenSource();

        PleaseWait pleasewait = new PleaseWait();
        pleasewait.Closed += PleaseWait_Closed;
        
        pleasewait.Show();

        // Perform your long-running task asynchronously
        await Task.Run(() => ProcessBookstoreExcel(cts.Token));

        pleasewait.Closed -= PleaseWait_Closed;

        pleasewait.Close();
        MessageBox.Show("Processing Complete!");
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        cts.Dispose();

        cts = null;
    }
}

private void PleaseWait_Closed(object sender, EventArgs e) => cts.Cancel();

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++) // Skip header row
        {
            cancellationToken.ThrowIfCancellation();

            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.ThrowIfCancellation();
        
        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
                });
            }
        }
    }

    WriteDiscrepancies(discrepancies);
    MessageBox.Show("Processing complete. Check the output file for discrepancies.");
}

private void WriteDiscrepancies(List<Discrepancy> discrepancies)
{
    var outputPath = Path.Combine(Path.GetDirectoryName(filePath), "discrepancies.xlsx");

    using (var package = new ExcelPackage())
    {
        var worksheet = package.Workbook.Worksheets.Add("Discrepancies");
        worksheet.Cells[1, 1].Value = "LineNumber";
        worksheet.Cells[1, 2].Value = "Publisher";
        worksheet.Cells[1, 3].Value = "Title";

        for (int i = 0; i < discrepancies.Count; i++)
        {
            worksheet.Cells[i + 2, 1].Value = discrepancies[i].LineNumber;
            worksheet.Cells[i + 2, 2].Value = discrepancies[i].Publisher;
            worksheet.Cells[i + 2, 3].Value = discrepancies[i].Title;
        }

        package.SaveAs(new FileInfo(outputPath));
    }
}

private void PleaseWait_FormClosing(object sender, FormClosingEventArgs e)
{
    var x = MessageBox.Show("Are you sure you want to really exit ? ", 
                            "Exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

    if (x == DialogResult.No) 
    {
        e.Cancel = true;
    }
    else
    {
        cts.Cancel();

        e.Cancel = false;
    }
}

Другие вопросы по теме