我如何能实现我的进步前1秒的延迟/取消显示对话框? [英] How can I implement a 1s delay before my progress/cancel dialog is shown?

查看:165
本文介绍了我如何能实现我的进步前1秒的延迟/取消显示对话框?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图构建一个进度/取消形式运行任何的await-能操作我的WinForms应用程序中使用,同时提供了一些进度信息和取消操作的机会给用户​​。

I'm trying to construct a progress/cancel form for use within my WinForms application that runs any await-able "operation", while providing the user with some progress information and an opportunity to cancel the operation.

由于形式是使用显示的ShowDialog(),这是一个模式的形式,很好地禁止下面的表格 - 所以我不需要惹禁用周围的一切其他窗体上的控件。

Because the form is shown using ShowDialog(), it's a modal form which nicely disables the form underneath - so I don't need to mess around with disabling all the controls on that other form.

他们的方法,我实现它,这我完全相信你撕撕成碎片:-),是要等待过程中操作的结果的 Form.Load 事件处理程序,然后关闭该窗体一旦操作要么完成(不管是因为它运行完成,被取消了,或者抛出一个异常)。

They way I've implemented it, which I fully expect you to rip to shreds :-), is to await the result of the operation during the Form.Load event handler, and then close the form once the operation is either completed (whether that be because it ran to completion, was cancelled, or raise an exception).

public partial class ProgressForm<T> : Form
{
    private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
    private Progress<string> _progress = new Progress<string>();
    private Func<IProgress<string>, CancellationToken, Task<T>> _operation = null;
    private Exception _exception = null;
    private T _result = default(T);


    public static T Execute(Func<IProgress<string>, CancellationToken, Task<T>> operation)
    {
        using (var progressForm = new ProgressForm<T>())
        {
            progressForm._operation = operation;

            progressForm.ShowDialog();

            if (progressForm._exception != null)
                throw progressForm._exception;
            else
                return progressForm._result;
        }
    }


    public ProgressForm()
    {
        InitializeComponent();

        this._progress.ProgressChanged += ((o, i) => this.ProgressLabel.Text = i.ToString());
    }

    private async void ProgressForm_Load(object sender, EventArgs e)
    {
        try
        {
            this._result = await this._operation(this._progress, this._cancellationTokenSource.Token);
        }
        catch (Exception ex) // Includes OperationCancelledException
        {
            this._exception = ex;
        }

        this.Close();
    }

    private void CancelXButton_Click(object sender, EventArgs e)
    {
        if (this._cancellationTokenSource != null)
            this._cancellationTokenSource.Cancel();
    }
}

这被称为是这样的:

int numberOfWidgets = ProgressForm<int>.Execute(CountWidgets);

...其中 CountWidgets()是的await-能够事情(在这种情况下,函数返回任务&LT; INT&GT; ,适当IProgress和的CancellationToken 参数)。

...where CountWidgets() is an await-able thing (in this case a function returning Task<int>, with appropriate IProgress and CancellationToken parameters).

到目前为止,它的工作原理pretty很好,但是有一个功能,我想补充。理想情况下,我想的形式仍然是(比如)一个第二不可见,因此,如果在操作完成真的很快有没有忽悠如图所示表格,然后立即再次隐藏起来。

So far it works pretty well, but there's one "feature" that I'd like to add. Ideally, I'd like the form to remain invisible for (say) one second, so that if the operation completes really quickly there's no "flicker" as the form is shown and then immediately hidden again.

所以,我的问题是如何引入1秒延时所示的形式之前。很显然,我还是想立即开始运作,但只要我等待操作的结果,我不再控制(这么说),因为控制将返回到<$的调用者C $ C> Form.Load 事件处理程序 - 这将继续呈现形式的工作

So, my question is how to introduce the 1s delay before the form is shown. Obviously, I still want to start the operation immediately, and yet as soon as I "await" the result of the operation, I'm no longer in control (so to speak) because control will be returned to the caller of the Form.Load event handler - which will continue the work of showing the form.

我怀疑,基本上我真的需要第二个线程,而我需要的操作,而我封锁的主UI线程在该线程中执行。 (我知道,阻塞UI线程令人难以接受的,但在这种情况下,我认为它实际上是什么,我需要)。

I suspect that essentially I really need a second thread, and that I need the operation to execute on that thread while I block the main UI thread. (I know that blocking the UI thread is frowned upon, but in this case I think it's actually what I need).

有创建线程,等那我不知道如何在新做的这么多不同的方式异步/等待的世界......

There are so many different ways of creating threads, etc. that I'm not sure how to do this in the new "async/await" world...

推荐答案

我认为你必须从你的对话,才能做到这一点单独拿出你的任务亚军。首先,响应进步,可以发出一个对话框取消:

I think you'll have to separate out your "task runner" from your "dialog" in order to do this. First, a dialog that responds to progress and can issue a cancel:

public partial class ProgressForm : Form
{
  private readonly CancellationTokenSource _cancellationTokenSource;

  public ProgressForm(CancellationTokenSource cancellationTokenSource, IProgress<string> progress)
  {
    InitializeComponent();

    _cancellationTokenSource = cancellationTokenSource;
    progress.ProgressChanged += ((o, i) => this.ProgressLabel.Text = i.ToString());
  }

  public static void ShowDialog(CancellationTokenSource cancellationTokenSource, IProgress<string> progress)
  {
    using (var progressForm = new ProgressForm(cancellationTokenSource, progress))
    {
        progressForm.ShowDialog();
    }
  }

  private void CancelXButton_Click(object sender, EventArgs e)
  {
    if (this._cancellationTokenSource != null)
      this._cancellationTokenSource.Cancel();
  }
}

接下来,实际的任务亚军:

Next, the actual "task runner":

public static class FriendlyTaskRunner
{
  public static async Task<T> Execute<T>(Func<CancellationToken, IProgress<string>, Task<T>> operation)
  {
    var cancellationTokenSource = new CancellationTokenSource();
    var progress = new Progress<string>();
    var timeout = Task.Delay(1000);
    var operationTask = operation(cancellationTokenSource.Token, progress);

    // Synchronously block for either the operation to complete or a timeout;
    //  if the operation completes first, just return the result.
    var completedTask = Task.WhenAny(timeout, operationTask).Result;
    if (completedTask == operationTask)
      return await operationTask;

    // Kick off a progress form and have it close when the task completes.
    using (var progressForm = new ProgressForm(cancellationTokenSource, progress))
    {
      operationTask.ContinueWith(_ => { progressForm.Close(); });
      progressForm.ShowDialog();
    }

    return await operationTask;
  }
}

请注意,同步阻塞UI线程可能导致死锁 - 在这种情况下,如果操作试图将同步回UI线程,它会被阻止,直到超时后 - 所以它不是一个真正的僵局,但很低效。

Please note that synchronously blocking the UI thread may cause deadlocks - in this case, if operation attempts to sync back to the UI thread, it would be blocked until after the timeout - so it's not a true "deadlock" but quite inefficient.

这篇关于我如何能实现我的进步前1秒的延迟/取消显示对话框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆