简体   繁体   English

异步显示对话框

[英]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()LoadDataAsyncClose()以避免出现 @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.

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