取消任务关闭窗口.如何检测任务是否同步返回? [英] Cancelling window closing with a task. How can I detect if task returned synchronously?

查看:76
本文介绍了取消任务关闭窗口.如何检测任务是否同步返回?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

有两种处理方法.

首先,是Herohtar在

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.

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

So let's walk through your code. First let's assume that something is true:

  1. MainWindow_OnClosing开始同步运行.
  2. ShouldCancelClose开始同步运行.
  3. 调用
  4. TryExit()并返回不完整的Task.
  5. await关键字看到不完整的Task并返回不完整的Task.控制权返回到MainWindow_OnClosing.
  6. MainWindow_OnClosing中的await看到不完整的Task,因此返回.由于返回类型为void,因此不返回任何内容.
  7. 控件将返回到表单,并且由于它无法等待MainWindow_OnClosing的其余部分,因此它假定事件处理程序已完成.
  8. 只要TryExit()完成,ShouldCancelCloseMainWindow_OnClosing的其余部分就会运行.
  9. 如果现在调用Close(),它将起作用,因为据表单所知,事件处理程序已在步骤6中完成.
  1. MainWindow_OnClosing starts running, synchronously.
  2. ShouldCancelClose starts running synchronously.
  3. TryExit() is called and returns an incomplete Task.
  4. The await keyword sees the incomplete Task and returns an incomplete Task. Control is returned to MainWindow_OnClosing.
  5. The await in MainWindow_OnClosing sees an incomplete Task, so it returns. Since the return type is void, it returns nothing.
  6. Control is returned to the form, and since it cannot await the rest of MainWindow_OnClosing, it assumes the event handler is finished.
  7. Whenever TryExit() finishes, the rest of ShouldCancelClose and MainWindow_OnClosing runs.
  8. If Close() is called now, it works, because as far as the form knows, the event handler finished at step 6.

现在让我们假设somethingfalse:

  1. MainWindow_OnClosing开始同步运行.
  2. ShouldCancelClose开始同步运行.
  3. ShouldCancelClose返回值为false的完整的Task.
  4. MainWindow_OnClosing中的await关键字会看到已完成的Task,并继续同步运行方法 .
  5. 调用Close()时,它会抛出异常,因为事件处理程序尚未完成运行.
  1. MainWindow_OnClosing starts running, synchronously.
  2. ShouldCancelClose starts running synchronously.
  3. ShouldCancelClose returns a completed Task with a value of false.
  4. The await keyword in MainWindow_OnClosing sees the completed Task and continues running the method synchronously.
  5. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆