Я пишу приложение 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()
Я думаю, мне следует использовать его, чтобы я мог выдать сообщение/исключение о том, что выполнение было отменено. Я не знаю, нужно это или нет. Пожалуйста, направляйте.
Вы можете запустить задачу асинхронно и реализовать функцию кнопки отмены для своей задачи в том же приложении. Или это должно быть другое приложение, запрашивающее у пользователя отмену?
@shingo Я проверил CancellationToken, но не был уверен, как буду использовать if (cancellationToken.IsCancellationRequested)
в ProcessBookStoreExcel()
.
@tom2051 Tom2051 Я хочу, чтобы, если пользователь закроет форму pleaseWait, он должен остановить выполнение после подтверждения. Я просто застрял в том, как обрабатывать e.Cancel с помощью async и жду
Вызовите cancellationToken.ThrowIfCancellationRequested()
в каждом цикле и добавьте catch(OperationCancelledException)
к месту вызова. Таким образом, вы узнаете, была ли задача отменена или нет.
Вам необходимо проверять IsCancellationRequested
перед каждым (важным) шагом.
В общем, с вашим кодом есть некоторые проблемы, однако я остановлюсь на вашем вопросе.
Чтобы решить проблему, вам необходимо реализовать логику удаления задачи, если так решит пользователь, и для этого вы можете использовать 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;
}
}
Вы можете использовать CancellationTokenSource , возможный дубликат: Как отменить ожидающую задачу?