C# 正在FormClosing事件中等待异步函数
我遇到了一个问题,我不能等待FormClosing事件中的异步函数,该函数将决定FormClose是否应继续。我创建了一个简单的示例,如果您关闭而不保存,它会提示您保存未保存的更改(很像使用记事本或microsoft word)。我遇到的问题是,当我等待异步保存函数时,它会在保存函数完成之前关闭表单,然后在完成后返回关闭函数并尝试继续。我唯一的解决方案是在调用SaveAsync之前取消关闭事件,然后如果保存成功,它将调用form.Close()函数。我希望有一个更干净的方法来处理这种情况 要复制该场景,请创建一个带有文本框(txtValue)、复选框(cbFail)和按钮(btnSave)的表单。这是表格的代码C# 正在FormClosing事件中等待异步函数,c#,.net-4.5,async-await,formclosing,C#,.net 4.5,Async Await,Formclosing,我遇到了一个问题,我不能等待FormClosing事件中的异步函数,该函数将决定FormClose是否应继续。我创建了一个简单的示例,如果您关闭而不保存,它会提示您保存未保存的更改(很像使用记事本或microsoft word)。我遇到的问题是,当我等待异步保存函数时,它会在保存函数完成之前关闭表单,然后在完成后返回关闭函数并尝试继续。我唯一的解决方案是在调用SaveAsync之前取消关闭事件,然后如果保存成功,它将调用form.Close()函数。我希望有一个更干净的方法来处理这种情况 要复制
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.
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统数据;
使用系统图;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows.Forms;
名称空间TestZ
{
公共部分类Form1:Form
{
字符串cleanValue=“”;
公共表格1()
{
初始化组件();
}
公共图书馆
{
返回值(txtValue.Text!=cleanValue);
}
公共无效ResetChangeState()
{
cleanValue=txtValue.Text;
}
私有异步无效btnSave\U单击(对象发送方,事件参数e)
{
//保存而不立即考虑结果
等待SaveAsync();
}
专用异步任务SaveAsync()
{
this.Cursor=Cursors.WaitCursor;
btnSave.Enabled=false;
txtValue.Enabled=false;
cbFail.Enabled=false;
任务工作=任务.工厂.开始新(()=>
{
//在后台线程上要做的工作
System.Threading.Thread.Sleep(3000);//假装努力工作。
如果(cbFail.Checked)
{
MessageBox.Show(“保存失败”);
返回false;
}
其他的
{
//值保存到数据库中,将当前表单状态标记为“干净”
MessageBox.Show(“保存成功”);
ResetChangeState();
返回true;
}
});
等待工作;
btnSave.Enabled=true;
txtValue.Enabled=true;
cbFail.Enabled=true;
this.Cursor=Cursors.Default;
返回返回;
}
私有异步无效Form1\u FormClosing(对象发送方,FormClosingEventArgs e)
{
if(HasChanges())
{
DialogResult result=MessageBox.Show(“存在未保存的更改。是否要在关闭前保存?”,“未保存的更改”,MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question);
if(result==System.Windows.Forms.DialogResult.Yes)
{
//这就是我想要处理它的方式——但它会在表单等待Save()完成时关闭表单。
//bool SaveSuccessful=等待保存();
//如果(!SaveSuccessful)
//{
//e.取消=真;
//}
//我必须这样处理:
e、 取消=真;
bool SaveSuccessful=等待SaveAsync();
如果(保存成功)
{
这个。关闭();
}
}
else if(result==System.Windows.Forms.DialogResult.Cancel)
{
e、 取消=真;
}
//如果他们点击“否”,只需关闭表单。
}
}
}
}
2013年5月23日编辑
人们会问我为什么要这么做,这是可以理解的
这样做。我们库中的数据类通常具有Save,
加载、新建、删除设计为异步运行的函数
(请参见SaveAsync作为示例)。事实上,我并不太在乎
在FormClosing事件中异步运行函数。但如果
用户希望在关闭表单之前保存,我需要它等待
查看保存是否成功。如果保存失败,那么我希望它
取消表单关闭事件。我只是在寻找最干净的方法
处理这件事
您不能阻止表单使用async/await关闭。你可以得到奇怪的结果 我要做的是创建一个
线程
,并将其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);
}
为什么必须涉及异步行为?这听起来像是必须以线性方式发生的事情。。我发现最简单的解决方案通常是正确的 除了下面的代码之外,您还可以让主线程睡眠一两秒钟,并让异步线程在主线程中设置一个标志
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;
}
在我看来,最好的答案是取消表格的关闭。总是。取消它,以您想要的方式显示您的对话框,用户完成对话框后,按编程方式关闭表单 我是这样做的:
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;
}
}
请务必注意对wait Task.Yield()的调用。没有必要
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;
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
Form decoy = new Form()
{
ControlBox = false,
StartPosition = FormStartPosition.CenterParent,
Size = new Size(300, 100),
Text = Text, // current window caption
};
Label label = new Label()
{
Text = "Saving...",
TextAlign = ContentAlignment.MiddleCenter,
Dock = DockStyle.Fill,
};
decoy.Controls.Add(label);
var t = Task.Run(async () =>
{
try
{
// keep form open if saving fails
e.Cancel = !await SaveAsync();
}
finally
{
decoy.Invoke(new MethodInvoker(decoy.Hide));
}
});
decoy.ShowDialog(this);
t.Wait(); //TODO: handle Exceptions
}