繁体   English   中英

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

[英]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该方法将同步继续

所以让我们来看看你的代码。 首先让我们假设somethingtrue

  1. MainWindow_OnClosing开始同步运行。
  2. ShouldCancelClose开始同步运行。
  3. TryExit()被调用并返回一个不完整的Task
  4. await关键字看到不完整的Task并返回一个不完整的Task 控制权返回到MainWindow_OnClosing
  5. MainWindow_OnClosing中的await看到一个不完整的Task ,所以它返回。 由于返回类型是void ,所以它什么也不返回。
  6. 控制权返回到窗体,由于它不能等待MainWindow_OnClosing的 rest,它假定事件处理程序已完成。
  7. 每当TryExit()完成时, ShouldCancelCloseMainWindow_OnClosing的 rest 就会运行。
  8. 如果现在调用Close() ,它可以工作,因为据表单所知,事件处理程序在步骤 6 完成

现在让我们假设somethingfalse的:

  1. MainWindow_OnClosing开始同步运行。
  2. ShouldCancelClose开始同步运行。
  3. ShouldCancelClose返回值为false的已完成Task
  4. MainWindow_OnClosing中的await关键字看到已完成的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();
        }
    }
}

信息很明确:您不能在关闭操作期间调用 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM