取消任务关闭窗口。如何检测任务是否同步返回?

我遵循一种相当常见的模式来使用异步对话框方法确认/取消我的主窗口关闭。但是,在我调用来呈现对话框的异步任务中,在某些情况下,我立即返回布尔值,而不是等待对话框任务方法的返回。在这些情况下,会引发异常:

  

System.InvalidOperationException:“无法在窗口关闭时将“可见性”设置为“可见”或调用Show,ShowDialog,Close或WindowInteropHelper.EnsureHandle。”

似乎这是因为异步任务正在同步返回并在窗口上调用Close(),而不是将其余代码作为继续。除了将try(catch)中包装Close()或在返回布尔值之前在函数中添加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;
}
baanwcw 回答:取消任务关闭窗口。如何检测任务是否同步返回?

例外情况是,Close()事件在运行过程中无法调用OnClosing。我想你明白。

有两种处理方法。

首先,即Herohtar在await Task.Yield()使用的评论中提到的答案。

更具体地说,密钥正在等待任何不完整的Task

原因是因为 async方法开始同步运行,就像其他任何方法一样。 await关键字仅在输入不完整的Task时才有意义。如果给定了已经完成的Task,则该方法将同步进行

因此,让我们遍历您的代码。首先,我们假设somethingtrue

  1. MainWindow_OnClosing开始同步运行。
  2. ShouldCancelClose开始同步运行。
  3. TryExit()被调用并返回不完整的Task
  4. 关键字await看到不完整的Task,并返回不完整的Task。控制权返回到MainWindow_OnClosing
  5. await中的MainWindow_OnClosing看到不完整的Task,因此返回。由于返回类型为void,因此不返回任何内容。
  6. 控件将返回到表单,并且由于它无法等待MainWindow_OnClosing的其余部分,因此它假定事件处理程序已完成。
  7. TryExit()完成后,ShouldCancelCloseMainWindow_OnClosing的其余部分都会运行。
  8. 如果现在调用Close(),则它可以工作,因为据表单所知,事件处理程序已在步骤6中完成

现在让我们假设somethingfalse

  1. MainWindow_OnClosing开始同步运行。
  2. ShouldCancelClose开始同步运行。
  3. ShouldCancelClose返回值为Task的完整false
  4. await中的MainWindow_OnClosing关键字会看到已完成的Task,并继续同步运行方法
  5. 调用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();
        }
    }
}
本文链接:https://www.f2er.com/3156799.html

大家都在问