我遇到了一个问题,在FormClosing事件中无法等待异步函数完成,这个函数将决定窗体关闭是否应该继续。我创建了一个简单的示例,如果您在没有保存的情况下关闭(就像记事本或Microsoft Word一样),会提示您保存未保存的更改。我遇到的问题是,当我等待异步的保存函数时,它会在保存函数完成之前关闭窗体,然后在完成后返回到关闭函数并尝试继续。我的唯一解决方案是在调用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
人们问我为什么要这样做是可以理解的。我们库中的数据类经常会有Save、Load、New、Delete等函数,这些函数都设计成可以异步运行(例如SaveAsync)。我并不在意在FormClosing事件中特别地以异步方式运行函数。但是,如果用户想在关闭表单之前保存,我需要它等待并查看保存是否成功。如果保存失败,则我希望取消表单关闭事件。我只是在寻找最简洁的方法来处理这个问题。
await Task.Yield();
,否则如果SaveAsync()
同步完成,可能会出现异常。 - Theodor Zoulias