Wpf子表单,OnClosing事件并等待

时间:2017-07-25 17:30:00

标签: wpf async-await formclosing

我从父表单启动了一个子表单,其中包含:

ConfigForm cfg = new ConfigForm();
cfg.ShowDialog();

此子窗体用于配置某些应用程序参数。 我想检查是否有一些未保存的更改,如果是,请警告用户。 所以我的On OnClosing事件以这种方式声明:

private async void ChildFormClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
    // Here i call a function that compare the current config with the saved config
    bool isUptated = CheckUnsavedChanges();

    // If updated is false, it means that there are unsaved changes...
    if (!isUpdated)
    {
         e.Cancel = true;

        // At this point i create a MessageDialog (Mahapps) to warn the user about unsaved changes...
        MessageDialogStyle style = MessageDialogStyle.AffirmativeAndNegative;

        var metroDialogSettings = new MetroDialogSettings()
        {
            AffirmativeButtonText = "Close",
            NegativeButtonText = "Cancel"
        };

        var result = await this.ShowMessageAsync("Config", "There are unsaved changes, do you want to exit?", style, metroDialogSettings);

        // If we press Close, we want to close child form and go back to parent...
        if (result == MessageDialogResult.Affirmative)
        {
            e.Cancel = false;
        }
    }
}

我的逻辑说,如果我将e.cancel声明为false,它将继续关闭表单,但不会发生,子表单仍然打开。

我的猜测是异步调用正在做一些我不理解的事情,因为如果我以这种方式声明ChildFormClosing:

private async void ChildFormClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
    bool isUptated = CheckUnsavedChanges();

    e.Cancel = true;

    if (!isUpdated)
    {
        MessageDialogStyle style = MessageDialogStyle.AffirmativeAndNegative;

        var metroDialogSettings = new MetroDialogSettings()
        {
            AffirmativeButtonText = "Close",
            NegativeButtonText = "Cancel"
        };

        var result = await this.ShowMessageAsync("Config", "There are unsaved changes, do you want to exit?", style, metroDialogSettings);

        if (result == MessageDialogResult.Affirmative)
        {
            e.Cancel = false;
        }
    }
    else
    {
        e.Cancel = false;
    }
}

最后的其他e.Cancel = false工作,子表单已关闭...

有任何线索吗? 谢谢!

1 个答案:

答案 0 :(得分:4)

由于此方法是窗口的事件处理程序,因此它将在UI线程上调用,因此不需要异步显示消息框。

至于您所看到的奇怪行为,这与事件处理程序中的await有关。当您await进行方法调用时,实际发生的事情是await之前的所有内容都正常执行,但一旦await语句达到控制权,控制权就会返回给调用者。一旦await的方法返回,则执行原始方法的其余部分。

触发OnClosing事件的代码可能不是为异步事件处理程序设计的,因此它假设如果事件处理程序返回,它已完成它需要做的任何工作。由于您的事件处理程序在方法调用CancelEventArgs.Cancel之前将true设置为await,因此事件处理程序的调用方看到它设置为true,因此它不会#39;关闭表格。

这就是显示消息框同步工作的原因:整个方法在控制权返回给调用者之前执行,因此CancelEventArgs.Cancel始终设置为其预期值。

Raymond Chen最近发表了两篇关于async可能有趣的文章:Crash course in async and awaitThe perils of async void。第二篇文章描述了为什么async事件处理程序往往不按预期方式工作。