调用 ShowDialog() 后继续执行代码

Posted

技术标签:

【中文标题】调用 ShowDialog() 后继续执行代码【英文标题】:Continue code execution after calling ShowDialog() 【发布时间】:2021-09-09 09:40:07 【问题描述】:

我试图在 else 代码继续在后台执行时打开一个加载窗口,并在需要时关闭它(不使用线程)。

类似这样的:

LoadingWindow.ShowDialog();
Thread.Sleep(2000);     //Simulates slowness
AnotherForm.ShowDialog();
LoadingWindow.Close(); //After AnotherForm displayed.

我不能只使用LoadingWindow.Show(); 继续执行,因为在LoadingWindow.Show(); 之后的代码执行之前,LoadingWindow 将无法正确显示。

我有一个自定义 Async Sh​​owDialog 方法 ShowDialogAsync();,但问题是 await 直到 AnotherForm.ShowDialog(); 完成。

我试过了:

var LoadingTask = LoadingWindow.ShowDialogAsync();
Thread.Sleep(2000);     //Simulates slowness
//await AnotherForm.ShowDialogAsync();    //Not worked
//AnotherForm.ShowDialog();               //Not worked
//AnotherForm.Show();                     //Not Worked
LoadingWindow.Close();
await LoadingTask;

这只能与 await 一起用于简单的方法:

var LoadingTask = LoadingWindow.ShowDialogAsync();
var data = await LoadDataAsync();
LoadingWindow.Close();
await LoadingTask;

//Sample simple method
private void LoadDataAsync()

        await Task.Delay(2000);
        return 10;

ShowDialogAsync:

public static async Task<DialogResult> ShowDialogAsync(this Form @this)

    await Task.Yield();
    if (@this.IsDisposed)
        return DialogResult.OK;
    return @this.ShowDialog();

【问题讨论】:

能否在问题中包含自定义的ShowDialogAsync 方法? @TheodorZoulias 我按要求添加了。这是一个使用Task.Yield(); 的简单返回方法,如果这个问题对你有意义,你可以给我一个答复。 由于您长期运行非 UI 工作来执行解决方案是在后台线程中执行,而不是在 UI 线程中。您说您不想这样做,但这就是该平台为您设计的方式。只是说你不想改变这一点。 UI 线程用于更新您的 UI,而不是执行长时间运行的非 UI 工作,如果不遵循该设计,您只会为自己制造大量问题。 ShowDialogAsync 看起来像 Windows 窗体方法。但是标签是WPF。项目的真实类型是什么? @aepot 我添加了 wpf 标签,因为 LoadingWindow 字段带有 wpf 命名法。但我不确定,可能是 WinForms。希望OP会澄清。 【参考方案1】:

作为linked answer 的作者,我感到被召唤:) 我想我理解您遇到的问题。尝试以下方法,查看内联的 cmets:

// on the main message loop of the UI thread
var LoadingTask = LoadingWindow.ShowDialogAsync();

// dialog isn't shown here yet, yield to the message loop
await Task.Yield();

// now ShowDialog has been called and the dialog is visible,
// we're now on the nested message loop run by ShowDialog
await Task.Run(() => 

  // on a thread pool thread, do the work here
  Thread.Sleep(2000);
);

// on the UI thread, still on the nested message loop of ShowDialog
// close the dialog
LoadingWindow.Close();

// still on the nested message loop, despite the window has closed
await LoadingTask;

// this is when ShowDialog method has returned,
// and we're back to the main message loop of the UI thread

不要在 UI 线程上使用Thread.Sleep 或任何其他长时间运行的同步代码,即使在进行实验时也是如此。对话框异步显示。 Thread.Sleep 阻止消息循环,ShowDialog 没有机会在您期望的时候执行。

这有助于理解在这种情况下Task.Yield 实际上是does behind the sence。 await Task.Yield() 之后的继续代码在消息循环的某些未来迭代之后才会神奇地执行。当您在 UI 线程上进行阻塞调用时,将不会有未来的迭代。这就是线程池和Task.Run 的用途。

否则,延续回调将只是坐在由WindowsFormsSynchronizationContextDispatcherSynchronizationContext 管理的队列中。

【讨论】:

Asynchronous programming,供参考。

以上是关于调用 ShowDialog() 后继续执行代码的主要内容,如果未能解决你的问题,请参考以下文章

WindowsForm多窗体多窗体传值控件数据绑定--12月8日

计次循环如何让他执行完我调用的线程,再继续循环

showdialog()的窗体点击message的按钮后,窗体也关闭了?

ThreadThreading.TimerTask中ShowDialog()方法报错:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式”

ThreadThreading.TimerTask中ShowDialog()方法报错:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式”

ThreadThreading.TimerTask中ShowDialog()方法报错:“在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式”