仅在后台操作很长时才显示进度

时间:2011-08-04 14:32:35

标签: c# .net winforms background progress

我正在开发一个C#操作,我想显示一个模态进度对话框,但只有当一个操作很长时(例如,超过3秒)。我在后台线程中执行我的操作。

问题在于我事先并不知道该操作是长还是短。

某些软件IntelliJ有一个计时器方法。如果操作时间超过x时间,则显示一个对话框。

您认为这是一个很好的模式来实现这个目标吗?

  • 使用计时器等待UI线程,并在那里显示对话框?
  • 当我显示对话框时,我必须DoEvents()吗?

6 个答案:

答案 0 :(得分:4)

我将在这里做出第一选择并进行一些修改:

首先在不同的线程中运行可能的长时间运行操作 然后运行一个不同的线程,通过一个带有超时的等待句柄检查第一个状态,等待它完成。如果超时触发,则显示进度条。

类似的东西:

private ManualResetEvent _finishLoadingNotifier = new ManualResetEvent(false);

private const int ShowProgressTimeOut = 1000 * 3;//3 seconds


private void YourLongOperation()
{
    ....

    _finishLoadingNotifier.Set();//after finish your work
}

private void StartProgressIfNeededThread()
{
    int result = WaitHandle.WaitAny(new WaitHandle[] { _finishLoadingNotifier }, ShowProgressTimeOut);

    if (result > 1)
    {
        //show the progress bar.
    } 
}

答案 1 :(得分:4)

这就是我要做的事情:

1)使用BackgroundWorker。

2)在调用方法RunWorkerAsync之前,将当前时间存储在变量中。

3)在DoWork事件中,您需要调用ReportProgress。在ProgressChanged事件中,检查时间是否已超过三秒。如果是,请显示对话框。

以下是BackgroundWorker的MSDN示例:http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

注意:一般来说,我同意Ramhound的评论。只需始终显示进度。但是如果您不使用BackgroundWorker,我会开始使用它。它会让你的生活更轻松。

答案 2 :(得分:2)

假设您使用DoPossiblyLongOperation()ShowProgressDialog()HideProgressDialog()方法,您可以使用TPL为您做繁重的工作:

var longOperation = new Task(DoPossiblyLongOperation).ContinueWith(() => myProgressDialog.Invoke(new Action(HideProgressDialog)));

if (Task.WaitAny(longOperation, new Task(() => Thread.Sleep(3000))) == 1)
    ShowProgressDialog();

答案 3 :(得分:0)

我会将进度对话框与后台活动分开,将我的UI逻辑与应用程序的其余部分分开。所以序列就是(这与IntelliJ所做的基本相同):

  1. UI启动后台操作(在BackgroundWorker中)并设置X秒的计时器
  2. 当计时器到期时,UI显示进度对话框(如果后台任务仍在运行)
  3. 当后台任务完成时,取消定时器并关闭对话框(如果有)
  4. 使用计时器而不是单独的线程可以提高资源效率。

答案 4 :(得分:0)

推荐的非阻塞解决方案,没有新的线程:

try
{
   var t = DoLongProcessAsync();
   if (await Task.WhenAny(t, Task.Delay(1000)) != t) ShowProgress();
   await t;
}
finally
{
   HideProgress();
}

答案 5 :(得分:0)

我从 Jalal Said 的回答中得到了这个想法。我需要超时或取消进度显示。我没有将附加参数(取消令牌句柄)传递给 WaitAny,而是将设计更改为依赖于 Task.Delay()

private const int ShowProgressTimeOut = 750;//750 ms seconds
public static void Report(CancellationTokenSource cts)
{
    Task.Run(async () =>
    {
        await Task.Delay(ShowProgressTimeOut);
        if (!cts.IsCancellationRequested)
        {
            // Report progress
        }
    });
}

像这样使用它;

private async Task YourLongOperation()
{
    CancellationTokenSource cts = new CancellationTokenSource();
    try
    {
        // Long running task on background thread
        await Task.Run(() => {
            Report(cts);
            // Do work
            cts.Cancel();
        });
    }
    catch (Exception ex) { }
    finally {cts.Cancel();}
}