У меня возникла проблема, когда я не могу ждать асинхронной функции внутри события FormClosing, которая будет определять, следует ли продолжать закрывать форму. Я создал простой пример, который предлагает вам сохранить несохраненные изменения, если вы закрываете без сохранения (так же, как с помощью блокнота или слова Microsoft). Проблема, с которой я столкнулся, заключается в том, что когда я жду функции асинхронного сохранения, она продолжает закрывать форму до завершения функции сохранения, затем она возвращается к закрывающей функции, когда она выполняется, и пытается продолжить. Мое единственное решение - отменить закрывающее событие перед вызовом SaveAsync, тогда, если сохранение будет успешным, оно вызовет функцию form.Close(). Я надеюсь, что есть более чистый способ справиться с этой ситуацией.
Чтобы воспроизвести сценарий, создайте форму с текстовым полем (txtValue), флажком (cbFail) и кнопкой (btnSave). Вот код для формы.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestZ
{
public partial class Form1 : Form
{
string cleanValue = "";
public Form1()
{
InitializeComponent();
}
public bool HasChanges()
{
return (txtValue.Text != cleanValue);
}
public void ResetChangeState()
{
cleanValue = txtValue.Text;
}
private async void btnSave_Click(object sender, EventArgs e)
{
//Save without immediate concern of the result
await SaveAsync();
}
private async Task<bool> SaveAsync()
{
this.Cursor = Cursors.WaitCursor;
btnSave.Enabled = false;
txtValue.Enabled = false;
cbFail.Enabled = false;
Task<bool> work = Task<bool>.Factory.StartNew(() =>
{
//Work to do on a background thread
System.Threading.Thread.Sleep(3000); //Pretend to work hard.
if (cbFail.Checked)
{
MessageBox.Show("Save Failed.");
return false;
}
else
{
//The value is saved into the database, mark current form state as "clean"
MessageBox.Show("Save Succeeded.");
ResetChangeState();
return true;
}
});
bool retval = await work;
btnSave.Enabled = true;
txtValue.Enabled = true;
cbFail.Enabled = true;
this.Cursor = Cursors.Default;
return retval;
}
private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (HasChanges())
{
DialogResult result = MessageBox.Show("There are unsaved changes. Do you want to save before closing?", "Unsaved Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == System.Windows.Forms.DialogResult.Yes)
{
//This is how I want to handle it - But it closes the form while it should be waiting for the Save() to complete.
//bool SaveSuccessful = await Save();
//if (!SaveSuccessful)
//{
// e.Cancel = true;
//}
//This is how I have to handle it:
e.Cancel = true;
bool SaveSuccessful = await SaveAsync();
if (SaveSuccessful)
{
this.Close();
}
}
else if (result == System.Windows.Forms.DialogResult.Cancel)
{
e.Cancel = true;
}
//If they hit "No", just close the form.
}
}
}
}
Редактировать 05/23/2013
Понятно, что люди спрашивали меня, почему я буду пытаться сделай это. В классах данных в наших библиотеках часто есть "Сохранить", Load, New, Delete, которые предназначены для асинхронного запуска (См. Пример SaveAsync). Мне на самом деле это не очень нравится запуская функцию асинхронно в событии FormClosing. Но если пользователь хочет сохранить до закрытия формы, мне нужно, чтобы она подождала и посмотрите, успешно или нет. Если сбой не удался, я хочу, чтобы он отмените событие закрытия формы. Я просто ищу самый чистый способ обрабатывайте это.