[英]Cancelling window closing with a task. How can I detect if task returned synchronously?
我正在遵循一种相当常见的模式来确认/取消我的主要 window 使用异步对话方法关闭。 但是,在我调用以呈现对话的异步任务中,在某些情况下,我会立即返回 boolean 值,而不是等待对话任务方法的返回。 在这些情况下会引发异常:
System.InvalidOperationException:“当 Window 正在关闭时,无法将 Visibility 设置为 Visible 或调用 Show、ShowDialog、Close 或 WindowInteropHelper.EnsureHandle。”
这似乎是因为异步任务正在同步返回并在 window 上调用 Close() 而不是调用代码的 rest 作为延续。 除了在 try/catch 中包装 Close() 或在返回我的 bool 之前在我的 function 中添加一个 Task.Delay() 之外,有没有办法检测我是否应该在我的 window 上调用 Close()? (即如果任务同步返回)
或者......我在概念上是否遗漏了异步/等待模式中的某些内容?
这是我的代码:
private bool _closeConfirmed;
private async void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
//check if flag set
if(!_closeConfirmed)
{
//use flag and always cancel first closing event (in order to allow making OnClosing work as as an async function)
e.Cancel = true;
var cancelClose = await mainViewModel.ShouldCancelClose();
if(!cancelClose)
{
_closeConfirmed = true;
this.Close();
}
}
}
这是异步 function 的样子:
public async Task<bool> ShouldCancelClose()
{
if(something)
{
var canExit = await (CurrentMainViewModel as AnalysisViewModel).TryExit();
if (!canExit) //if user cancels exit
return true;
//no exception
return false;
}
//this causes exception
return false;
}
例外是当OnClosing
事件正在运行时,您不能调用Close()
。 我想你明白这一点。
有两种方法可以处理这个问题。
首先,Herohtar 在使用await Task.Yield()
的评论中提到的答案。
更具体地说,关键是等待任何不完整的Task
。
原因是因为async
方法开始同步运行,就像任何其他方法一样。 await
关键字只有在给定一个不完整的Task
时才会做任何有意义的事情。 如果给定一个已经完成的Task
,该方法将同步继续。
所以让我们来看看你的代码。 首先让我们假设something
是true
:
MainWindow_OnClosing
开始同步运行。ShouldCancelClose
开始同步运行。TryExit()
被调用并返回一个不完整的Task
。await
关键字看到不完整的Task
并返回一个不完整的Task
。 控制权返回到MainWindow_OnClosing
。MainWindow_OnClosing
中的await
看到一个不完整的Task
,所以它返回。 由于返回类型是void
,所以它什么也不返回。MainWindow_OnClosing
的 rest,它假定事件处理程序已完成。TryExit()
完成时, ShouldCancelClose
和MainWindow_OnClosing
的 rest 就会运行。Close()
,它可以工作,因为据表单所知,事件处理程序在步骤 6 完成。 现在让我们假设something
是false
的:
MainWindow_OnClosing
开始同步运行。ShouldCancelClose
开始同步运行。ShouldCancelClose
返回值为false
的已完成Task
。MainWindow_OnClosing
中的await
关键字看到已完成的Task
并继续同步运行该方法。Close()
时,它会抛出异常,因为事件处理程序尚未完成运行。 因此,使用await Task.Yield()
只是一种等待不完整内容的方法,以便将控制权返回给表单,因此它认为事件处理程序已经完成。
其次,如果您知道没有异步代码运行,那么您可以依靠e.Cancel
来取消关闭与否。 您可以通过在知道任务是否完成之前不等待Task
来进行检查。 这可能看起来像这样:
private bool _closeConfirmed;
private async void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
//check if flag set
if(!_closeConfirmed)
{
var cancelCloseTask = mainViewModel.ShouldCancelClose();
//Check if we were given a completed Task, in which case nothing
//asynchronous happened.
if (cancelCloseTask.IsCompleted)
{
if (await cancelCloseTask)
{
e.Cancel = true;
}
else
{
_closeConfirmed = true;
}
return;
}
//use flag and always cancel first closing event (in order to allow making OnClosing work as as an async function)
e.Cancel = true;
if(!await cancelCloseTask)
{
_closeConfirmed = true;
this.Close();
}
}
}
信息很明确:您不能在关闭操作期间调用 this.Close()...而且您不必...尝试这种方式:
private async void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
bool confirmClosing= await ShouldCancelClose();
if(!confirmClosing) e.Cancel = true;
// else .. do nothing ... let it goes and the window will be closed!
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.