Search code examples
c#winforms

Cancel the execution on Form Closing C#


I am writing a winform application that reads a large excel file, does some processing, and writes output in some other excel file. While the main form is doing its task, my app shows a please wait form. I want to implement a functionality when if the user clicks on the cross sign of the pleasewait form, he should be prompted if he really wishes to exit, if he clicks yes, then the application should stop executing. I dont want to use application.Exit(0). I just want to stop the execution. The main form should still display and if the user begins to process the file again then it should happen from the beginning.

Here is my code -

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));
    }
}



Please Wait.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;
    }
}

I am confused how to capture e.Cancel in Home.cs correctly.

Edit :

I read about CancellationToken but then I am not sure how I use

if (cancellationToken.IsCancellationRequested) in the ProcessBookStoreExcel()

I think I should use it so that I can throw a message/exception that execution was cancelled. I dont know if its necessary or not. Please guide.


Solution

  • In general there are some problems with your code, however I will center in your question.

    To solve the problem you have to implement logic to delete the task cuando the user decides so and for that you can use a CancellationToken.

    So you declare a CancellationTokenSource in the Home.cs class, which you will use in case the PleaseWait form is closed before the task ends to cancel it.

    Keep in mind that I've used the ThrowIfCancellationRequested method, which throws an exception, though you can also use the IsCancellationRequested propiety.

    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;
        }
    }