【发布时间】:2013-05-20 19:03:17
【问题描述】:
我遇到了一个问题,我无法在 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
人们会问我为什么要尝试这样做是可以理解的 做这个。我们库中的数据类通常会保存, 设计为异步运行的加载、新建、删除函数 (以 SaveAsync 为例)。我其实并没有那么在意 专门在 FormClosing 事件中异步运行该函数。但如果 用户想要在关闭表单之前保存,我需要它等待并 看看保存是否成功。如果保存失败,那么我希望它 取消表单关闭事件。我只是在寻找最干净的方法 处理这个。
【问题讨论】:
-
在程序终止前一毫秒触发的事件中使用 await 将无法正常工作。你必须让它活着。
-
我认为您现在还没有得到答案的事实似乎表明您目前正在做的可能是最好的方法,或者至少足够好。它看起来不太漂亮,但我能想到的唯一问题是有人可以在当前保存更改时单击关闭按钮并选择保存;您需要处理它并确保在发生这种情况时只调用一次 save。
-
你可能是对的,也许没有更好的方法。至于防止用户在保存时单击保存按钮,在我的真实应用程序中我正在处理它。保持表单响应时要注意的好事情。
-
相关:.NET Async in shutdown methods?。一个重要的细节是在以编程方式关闭表单之前
await Task.Yield();,否则如果SaveAsync()同步完成,您可能会遇到异常。
标签: c# .net-4.5 async-await formclosing