[英]Async ShowDialog
I'm using async/await to asynchronously load my data from database and during the loading process, I want to popup a loading form, it's just a simple form with running progress bar to indicate that there's a running process.我正在使用 async/await 从数据库中异步加载我的数据,在加载过程中,我想弹出一个加载表单,它只是一个带有运行进度条的简单表单,表明有一个正在运行的进程。 After data has been loaded, the dialog will automatically be closed.
加载数据后,对话框将自动关闭。 How can I achieve that ?
我怎样才能做到这一点? Below is my current code:
以下是我当前的代码:
protected async void LoadData()
{
ProgressForm _progress = new ProgressForm();
_progress.ShowDialog() // not working
var data = await GetData();
_progress.Close();
}
Updated:更新:
I managed to get it working by changing the code:我设法通过更改代码使其工作:
protected async void LoadData()
{
ProgressForm _progress = new ProgressForm();
_progress.BeginInvoke(new System.Action(()=>_progress.ShowDialog()));
var data = await GetData();
_progress.Close();
}
Is this the correct way or there's any better ways ?这是正确的方法还是有更好的方法?
Thanks for your help.谢谢你的帮助。
It's easy to implement with Task.Yield
, like below (WinForms, no exception handling for simplicity).使用
Task.Yield
很容易实现,如下所示(WinForms,为简单起见没有异常处理)。 It's important to understand how the execution flow jumps over to a new nested message loop here (that of the modal dialog) and then goes back to the original message loop (that's what await progressFormTask
is for):重要的是要了解执行流程如何在这里跳转到一个新的嵌套消息循环(模式对话框的那个),然后返回到原始消息循环(这就是
await progressFormTask
的用途):
namespace WinFormsApp
{
internal static class DialogExt
{
public static async Task<DialogResult> ShowDialogAsync(this Form @this)
{
await Task.Yield();
if (@this.IsDisposed)
return DialogResult.Cancel;
return @this.ShowDialog();
}
}
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
async Task<int> LoadDataAsync()
{
await Task.Delay(2000);
return 42;
}
private async void button1_Click(object sender, EventArgs e)
{
var progressForm = new Form() {
Width = 300, Height = 100, Text = "Please wait... " };
object data;
var progressFormTask = progressForm.ShowDialogAsync();
try
{
data = await LoadDataAsync();
}
finally
{
progressForm.Close();
await progressFormTask;
}
// we got the data and the progress dialog is closed here
MessageBox.Show(data.ToString());
}
}
}
Here's a pattern that uses Task.ContinueWith and should avoid any race condition with your use of the modal ProgressForm:这是一个使用 Task.ContinueWith 的模式,应该避免使用模态 ProgressForm 时出现任何竞争条件:
protected async void LoadDataAsync()
{
var progressForm = new ProgressForm();
// 'await' long-running method by wrapping inside Task.Run
await Task.Run(new Action(() =>
{
// Display dialog modally
// Use BeginInvoke here to avoid blocking
// and illegal cross threading exception
this.BeginInvoke(new Action(() =>
{
progressForm.ShowDialog();
}));
// Begin long-running method here
LoadData();
})).ContinueWith(new Action<Task>(task =>
{
// Close modal dialog
// No need to use BeginInvoke here
// because ContinueWith was called with TaskScheduler.FromCurrentSynchronizationContext()
progressForm.Close();
}), TaskScheduler.FromCurrentSynchronizationContext());
}
ShowDialog()
is a blocking call; ShowDialog()
是一个阻塞调用; execution will not advance to the await
statement until the dialog box is closed by the user.在用户关闭对话框之前,执行不会前进到
await
语句。 Use Show()
instead.请改用
Show()
。 Unfortunately, your dialog box will not be modal, but it will correctly track the progress of the asynchronous operation.不幸的是,您的对话框不会是模态的,但它会正确跟踪异步操作的进度。
Always call ShowDialog()
, LoadDataAsync
and Close()
in order to avoid IsDisposed
as in @noseratio 's answer.始终调用
ShowDialog()
、 LoadDataAsync
和Close()
以避免出现 @noseratio 的答案中的IsDisposed
。 So use Task.Yield()
to delay LoadDataAsync()
and not ShowDialog()
所以使用
Task.Yield()
来延迟LoadDataAsync()
而不是ShowDialog()
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
async Task<int> LoadDataAsync()
{
Console.WriteLine("Load");
await Task.Delay(2000);
return 42;
}
private async void button1_Click(object sender, EventArgs e)
{
var progressForm = new Form()
{
Width = 300,
Height = 100,
Text = "Please wait... "
};
async Task<int> work()
{
try
{
await Task.Yield();
return await LoadDataAsync();
}
finally
{
Console.WriteLine("Close");
progressForm.Close();
}
}
var task = work();
Console.WriteLine("ShowDialog");
progressForm.ShowDialog();
object data = await task;
// we got the data and the progress dialog is closed here
Console.WriteLine("MessageBox");
MessageBox.Show(data.ToString());
}
}
You could try the following:您可以尝试以下方法:
protected async void LoadData()
{
ProgressForm _progress = new ProgressForm();
var loadDataTask = GetData();
loadDataTask.ContinueWith(a =>
this.Invoke((MethodInvoker)delegate
{
_progress.Close();
}));
_progress.ShowDialog();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.