仅当后台操作较长时才显示进度

发布于 2024-11-27 22:38:51 字数 339 浏览 3 评论 0原文

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

问题是我事先不知道操作会是长还是短。

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

您认为实现这一点的好模式是什么?

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

I'm developing a C# operation and I would like to show a modal progress dialog, but only when an operation will be long (for example, more than 3 seconds). I execute my operations in a background thread.

The problem is that I don't know in advance whether the operation will be long or short.

Some software as IntelliJ has a timer aproach. If the operation takes more than x time, then show a dialog then.

What do you think that is a good pattern to implement this?

  • Wait the UI thread with a timer, and show dialog there?
  • Must I DoEvents() when I show the dialog?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

娇女薄笑 2024-12-04 22:38:52

这就是我要做的:

1)使用BackgroundWorker。

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

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

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

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

Here's what I'd do:

1) Use a BackgroundWorker.

2) In before you call the method RunWorkerAsync, store the current time in a variable.

3) In the DoWork event, you'll need to call ReportProgress. In the ProgressChanged event, check to see if the time has elapsed greater than three seconds. If so, show dialog.

Here is a MSDN example for the BackgroundWorker: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

Note: In general, I agree with Ramhound's comment. Just always display the progress. But if you're not using BackgroundWorker, I would start using it. It'll make your life easier.

白鸥掠海 2024-12-04 22:38:52

我将在此处选择第一个选择并进行一些修改:

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

像这样的东西:

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.
    } 
}

I will go with the first choice here with some modifications:

First run the possible long running operation in different thread.
Then run a different thread to check the first one status by a wait handle with timeout to wait it for finish. if the time out triggers there show the progress bar.

Something like:

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.
    } 
}
苦行僧 2024-12-04 22:38:52

假设您有 DoPossibilityLongOperation()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();

Assuming you have a DoPossiblyLongOperation(), ShowProgressDialog() and HideProgressDialog() methods, you could use the TPL to do the heavy lifting for you:

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

if (Task.WaitAny(longOperation, new Task(() => Thread.Sleep(3000))) == 1)
    ShowProgressDialog();
放肆 2024-12-04 22:38:52

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

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

Recommended non-blocking solution and no new Threads:

try
{
   var t = DoLongProcessAsync();
   if (await Task.WhenAny(t, Task.Delay(1000)) != t) ShowProgress();
   await t;
}
finally
{
   HideProgress();
}
情深缘浅 2024-12-04 22:38:52

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

  1. UI 启动后台操作(在 BackgroundWorker 中)并设置 X 秒的计时器
  2. 当计时器到期时,UI 显示进度对话框(如果后台任务是仍在运行)
  3. 当后台任务完成时,计时器将被取消并关闭对话框(如果有)。

使用计时器而不是单独的线程会更节省资源。

I would keep the progress dialog separate from the background activity, to separate my UI logic from the rest of the application. So the sequence would be (This is essentially the same as what IntelliJ does):

  1. UI starts the background operation (in a BackgroundWorker) and set up a timer for X seconds
  2. When the timer expires UI shows the progress dialog (if the background task is still running)
  3. When the background task completes the timer is cancelled and the dialog (if any) is closed

Using a timer instead of a separate thread is more resource-efficient.

掩耳倾听 2024-12-04 22:38:52

我从 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();}
}

I got the idea from Jalal Said answer. I required the need to timeout or cancel the progress display. Instead of passing an additional parameter (cancellation token handle) to the WaitAny I changed the design to depend on 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
        }
    });
}

Use it like so;

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();}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文