[英]Async ShowDialog
我正在使用 async/await 從數據庫中異步加載我的數據,在加載過程中,我想彈出一個加載表單,它只是一個帶有運行進度條的簡單表單,表明有一個正在運行的進程。 加載數據后,對話框將自動關閉。 我怎樣才能做到這一點? 以下是我當前的代碼:
protected async void LoadData()
{
ProgressForm _progress = new ProgressForm();
_progress.ShowDialog() // not working
var data = await GetData();
_progress.Close();
}
更新:
我設法通過更改代碼使其工作:
protected async void LoadData()
{
ProgressForm _progress = new ProgressForm();
_progress.BeginInvoke(new System.Action(()=>_progress.ShowDialog()));
var data = await GetData();
_progress.Close();
}
這是正確的方法還是有更好的方法?
謝謝你的幫助。
使用Task.Yield
很容易實現,如下所示(WinForms,為簡單起見沒有異常處理)。 重要的是要了解執行流程如何在這里跳轉到一個新的嵌套消息循環(模式對話框的那個),然后返回到原始消息循環(這就是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());
}
}
}
這是一個使用 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()
是一個阻塞調用; 在用戶關閉對話框之前,執行不會前進到await
語句。 請改用Show()
。 不幸的是,您的對話框不會是模態的,但它會正確跟蹤異步操作的進度。
始終調用ShowDialog()
、 LoadDataAsync
和Close()
以避免出現 @noseratio 的答案中的IsDisposed
。 所以使用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());
}
}
您可以嘗試以下方法:
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.