取消任务关闭窗口.如何检测任务是否同步返回? [英] Cancelling window closing with a task. How can I detect if task returned synchronously?
问题描述
我遵循一种相当常见的模式来使用异步对话框方法确认/取消我的主窗口关闭.但是,在我调用来呈现对话框的异步任务中,在某些情况下,我会立即返回布尔值,而不是等待对话框任务方法的返回.在这些情况下,会引发异常:
I am following a fairly common pattern for confirming/canceling my main window closing with an async dialog method. However, in the async task that I call to present the dialog, there are conditions where I return a boolean value immediately instead awaiting the return of a dialog task method. In those cases an exception is thrown:
System.InvalidOperationException:无法在窗口关闭时将可见性"设置为可见"或调用Show,ShowDialog,Close或WindowInteropHelper.EnsureHandle."
System.InvalidOperationException: 'Cannot set Visibility to Visible or call Show, ShowDialog, Close, or WindowInteropHelper.EnsureHandle while a Window is closing.'
似乎这是因为异步任务正在同步返回并在窗口上调用Close(),而不是将其余代码作为延续.除了将try(catch)中的Close()包装或在返回布尔值之前在函数中添加Task.Delay()之外,还有什么方法可以检测是否应该在窗口上调用Close()? (即,如果任务同步返回)
It seems that this is because the async task is returning synchronously and calling Close() on the window instead of calling the rest of the code as a continuation. Besides just wrapping Close() in a try/catch or adding a Task.Delay() in my function before returning my bool, is there a way to detect if I should call Close() on my window? (ie. if the task returned synchronously)
或者...我在概念上是否缺少异步/等待模式中的某些内容?
Or...am I conceptually missing something in the async/await pattern?
这是我的代码:
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();
}
}
}
这是异步函数的样子:
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()
.我想你明白.
The exception is saying that you cannot call Close()
while the OnClosing
event is in the process of running. I think you understand that.
有两种处理方法.
First, the answer mentioned by Herohtar in the comments used await Task.Yield()
.
更具体地说,密钥正在等待任何不完整的Task
.
More specifically, the key is awaiting any incomplete Task
.
原因是因为 async
方法开始同步运行,就像其他任何方法一样.如果给await
关键字提供了不完整的Task
,则仅做任何有意义的事情.如果给定的Task
已经完成,则该方法将同步继续.
The reason is because async
methods start running synchronously, just like any other method. The await
keyword only does anything significant if it is given an incomplete Task
. If it is given a Task
that is already completed, the method continues synchronously.
因此,让我们遍历您的代码.首先,我们假设something
是true
:
So let's walk through your code. First let's assume that something
is true
:
-
MainWindow_OnClosing
开始同步运行. -
ShouldCancelClose
开始同步运行.
调用 -
TryExit()
并返回不完整的Task
. -
await
关键字看到不完整的Task
并返回不完整的Task
.控制权返回到MainWindow_OnClosing
. -
MainWindow_OnClosing
中的await
看到不完整的Task
,因此返回.由于返回类型为void
,因此不返回任何内容. - 控件将返回到表单,并且由于它无法等待
MainWindow_OnClosing
的其余部分,因此它假定事件处理程序已完成. - 只要
TryExit()
完成,ShouldCancelClose
和MainWindow_OnClosing
的其余部分就会运行. - 如果现在调用
Close()
,它将起作用,因为据表单所知,事件处理程序已在步骤6中完成.
MainWindow_OnClosing
starts running, synchronously.ShouldCancelClose
starts running synchronously.TryExit()
is called and returns an incompleteTask
.- The
await
keyword sees the incompleteTask
and returns an incompleteTask
. Control is returned toMainWindow_OnClosing
. - The
await
inMainWindow_OnClosing
sees an incompleteTask
, so it returns. Since the return type isvoid
, it returns nothing. - Control is returned to the form, and since it cannot await the rest of
MainWindow_OnClosing
, it assumes the event handler is finished. - Whenever
TryExit()
finishes, the rest ofShouldCancelClose
andMainWindow_OnClosing
runs. - If
Close()
is called now, it works, because as far as the form knows, the event handler finished at step 6.
现在让我们假设something
是false
:
-
MainWindow_OnClosing
开始同步运行. -
ShouldCancelClose
开始同步运行. -
ShouldCancelClose
返回值为false
的完整的Task
. -
MainWindow_OnClosing
中的await
关键字会看到已完成的Task
,并继续同步运行方法 . - 调用
Close()
时,它会抛出异常,因为事件处理程序尚未完成运行.
MainWindow_OnClosing
starts running, synchronously.ShouldCancelClose
starts running synchronously.ShouldCancelClose
returns a completedTask
with a value offalse
.- The
await
keyword inMainWindow_OnClosing
sees the completedTask
and continues running the method synchronously. - When
Close()
is called, it throws the exception because the event handler has not finished running.
因此,使用await Task.Yield()
只是等待某些不完整的一种方法,以便将控件返回到表单,以便它认为事件处理程序已完成.
So using await Task.Yield()
is just a way to await something incomplete so that control is returned to the form so it thinks the event handler has finished.
第二,如果您知道没有异步代码在运行,则可以依靠e.Cancel
取消关闭.您可以通过等待Task
直到知道它是否完成来进行检查.可能看起来像这样:
Second, if you know that no asynchronous code has run, then you can rely on e.Cancel
to cancel the closing or not. You can check by not awaiting the Task
until you know if it's complete or not. That could look something like this:
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();
}
}
}
这篇关于取消任务关闭窗口.如何检测任务是否同步返回?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!