等待FormClosing事件中的异步函数

我遇到了一个问题,我无法等待FormClosing事件中的异步函数,这将决定表单关闭是否应该继续。 我创建了一个简单的示例,如果您在未保存的情况下关闭,则会提示您保存未保存的更改(非常类似于记事本或Microsoft Word)。 我遇到的问题是,当我等待异步保存function时,它会在保存function完成之前关闭表单,然后在完成后返回结束function并尝试继续。 我唯一的解决方案是在调用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 SaveAsync() { this.Cursor = Cursors.WaitCursor; btnSave.Enabled = false; txtValue.Enabled = false; cbFail.Enabled = false; Task work = Task.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事件中异步运行函数。 但是如果用户想要在关闭表单之前保存,我需要等待,看看保存是否成功。 如果保存失败,那么我希望它取消表单结束事件。 我只是在寻找最干净的方法来处理这个问题。

在我看来,最好的答案是取消表格。 总是。 取消它,根据需要显示对话框,一旦用户完成对话框,以编程方式关闭表单。

这是我做的:

 async void Window_Closing(object sender, CancelEventArgs args) { var w = (Window)sender; var h = (ObjectViewModelHost)w.Content; var v = h.ViewModel; if (v != null && v.IsDirty) { args.Cancel = true; w.IsEnabled = false; // caller returns and window stays open await Task.Yield(); var c = await interaction.ConfirmAsync( "Close", "You have unsaved changes in this window. If you exit they will be discarded.", w); if (c) w.Close(); // doesn't matter if it's closed w.IsEnabled = true; } } 

您无法使用async / await保持表单关闭。 你可以得到奇怪的结果。

我要做的是创建一个Thread并将其IsBackground属性设置为false(默认情况下为false),以在表单关闭时保持进程处于活动状态。

 protected override void OnClosing(CancelEventArgs e) { e.Cancel = false; new Thread(() => { Thread.Sleep(5000); //replace this line to save some data..... MessageBox.Show("EXITED"); }).Start(); base.OnClosing(e); } 

为什么必须涉及异步行为? 这听起来像必须以线性方式发生。我发现最简单的解决方案通常是正确的解决方案。

除了下面的代码,您可以让主线程hibernate一两秒,并让异步线程在主线程中设置一个标志。

 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 == DialogResult.Yes) { e.Cancel = true; if(!Save()) { MessageBox.Show("Your work could not be saved. Check your input/config and try again"); e.Cancel = true; } } else if (result == DialogResult.Cancel) { e.Cancel = true; } } } 

当我尝试处理所有关闭事件异步时,我遇到了类似的问题。 我相信这是因为没有什么可以阻止主线程向前推进实际的FormClosingEvents。 只需在等待之后放入一些内联代码,它就可以解决问题。 在我的情况下,我保存当前状态,无论响应(等待响应)。 一旦用户响应,您可以轻松地让任务返回当前状态,以便妥善保存。

这对我有用:分离任务,询问退出确认,等待任务,一些内联代码。

  Task myNewTask = SaveMyCurrentStateTask(); //This takes a little while so I want it async in the background DialogResult exitResponse = MessageBox.Show("Are you sure you want to Exit MYAPPNAME? ", "Exit Application?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); await myNewTask; if (exitResponse == DialogResult.Yes) { e.Cancel = false; } else { e.Cancel = true; } 

如果在执行异步方法期间引发了exception,我需要中止关闭表单。

我实际上使用了Task.Run .Wait()Task.Run

 private void Example_FormClosing(object sender, FormClosingEventArgs e) { try { Task.Run(async () => await CreateAsync(listDomains)).Wait(); } catch (Exception ex) { MessageBox.Show($"{ex.Message}", "Attention", MessageBoxButtons.OK, MessageBoxIcon.Error); e.Cancel = true; } }