我遵循一种相当常见的模式,使用异步对话框方法确认/取消主窗口关闭。但是,在我调用来呈现对话框的异步任务中,在某些情况下我会立即返回布尔值,而不是等待对话框任务方法的返回。在这些情况下会抛出异常:
System.InvalidOperationException:“在窗口关闭时无法将可见性设置为可见或调用 Show、ShowDialog、Close 或 WindowInteropHelper.EnsureHandle。”
It seems这是因为异步任务同步返回并在窗口上调用 Close(),而不是调用其余代码作为延续。除了在 try/catch 中包装 Close() 或在返回 bool 之前在函数中添加 Task.Delay() 之外,还有其他方法可以检测我是否应该在窗口上调用 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();
}
}
}
异步函数如下所示:
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;
}
例外情况是你不能打电话Close()
而OnClosing
事件正在运行。我想你明白这一点。
有两种方法可以处理这个问题。
First,使用了 Herohtar 在评论中提到的答案await Task.Yield() https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.yield?view=netframework-4.8.
更具体地说,关键是等待任何不完整的Task
.
原因是因为async
方法开始同步运行,就像任何其他方法一样。这await
关键字只有在给出不完整的值时才会做任何有意义的事情Task
。如果给定一个Task
已经完成的,该方法同步继续.
那么让我们看一下您的代码。首先我们假设something
is true
:
-
MainWindow_OnClosing
开始同步运行。
-
ShouldCancelClose
开始同步运行。
-
TryExit()
被调用并返回一个不完整的Task
.
- The
await
关键字看到不完整Task
并返回一个不完整的Task
。控制权返回到MainWindow_OnClosing
.
- The
await
in MainWindow_OnClosing
看到不完整的Task
,所以它返回。由于返回类型是void
,它什么也不返回。
- 控制权返回到表单,并且由于它无法等待其余部分
MainWindow_OnClosing
,它假设事件处理程序已完成。
- 每当
TryExit()
完成,剩下的ShouldCancelClose
and MainWindow_OnClosing
runs.
- If
Close()
现在被调用,它有效,因为据表单所知,事件处理程序在第 6 步完成.
现在我们假设something
is false
:
-
MainWindow_OnClosing
开始同步运行。
-
ShouldCancelClose
开始同步运行。
-
ShouldCancelClose
返回一个已完成的Task
值为false
.
- The
await
关键字在MainWindow_OnClosing
看到完成的Task
并继续运行该方法同步地.
- When
Close()
被调用,它抛出异常因为事件处理程序尚未完成运行.
所以使用await Task.Yield()
只是一种等待的方式不完整的东西以便控制权返回到表单,因此它认为事件处理程序已完成。
Second,如果您知道没有异步代码运行,那么您可以依赖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();
}
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)