在 C# 中做一些工作时显示进度条? [英] Display progress bar while doing some work in C#?

查看:58
本文介绍了在 C# 中做一些工作时显示进度条?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在做一些工作时显示一个进度条,但这会挂起 UI 并且进度条不会更新.

I want to display a progress bar while doing some work, but that would hang the UI and the progress bar won't update.

我有一个带有 ProgressBar 的 WinForm ProgressForm,它将以 ma​​rquee 方式无限期地继续.

I have a WinForm ProgressForm with a ProgressBar that will continue indefinitely in a marquee fashion.

using(ProgressForm p = new ProgressForm(this))
{
//Do Some Work
}

现在有很多方法可以解决这个问题,比如使用BeginInvoke,等待任务完成并调用EndInvoke.或者使用 BackgroundWorkerThreads.

Now there are many ways to solve the issue, like using BeginInvoke, wait for the task to complete and call EndInvoke. Or using the BackgroundWorker or Threads.

我在使用 EndInvoke 时遇到了一些问题,但这不是问题.问题是你用来处理这种情况的最好和最简单的方法是什么,你必须向用户展示程序正在运行而不是没有响应,以及你如何用最简单的代码来处理这种情况,既高效又不会"t 泄漏,并且可以更新 GUI.

I am having some issues with the EndInvoke, though that's not the question. The question is which is the best and the simplest way you use to handle such situations, where you have to show the user that the program is working and not unresponsive, and how do you handle that with simplest code possible that is efficient and won't leak, and can update the GUI.

BackgroundWorker需要有多个函数,声明成员变量等,然后你还需要持有对ProgressBar Form的引用并处理它.

Like BackgroundWorker needs to have multiple functions, declare member variables, etc. Also you need to then hold a reference to the ProgressBar Form and dispose of it.

编辑:BackgroundWorker 不是答案,因为可能是我没有收到进度通知,这意味着不会调用 ProgressChanged 因为 DoWork 是对外部函数的单次调用,但我需要不断调用 Application.DoEvents(); 以使进度条保持旋转.

Edit: BackgroundWorker is not the answer because it may be that I don't get the progress notification, which means there would be no call to ProgressChanged as the DoWork is a single call to an external function, but I need to keep call the Application.DoEvents(); for the progress bar to keep rotating.

赏金是针对此问题的最佳代码解决方案.我只需要调用 Application.DoEvents() 以便 Marque 进度条可以工作,而辅助函数在主线程中工作,并且不返回任何进度通知.我从不需要 .NET 魔术代码来自动报告进度,我只需要一个比 :

The bounty is for the best code solution for this problem. I just need to call Application.DoEvents() so that the Marque progress bar will work, while the worker function works in the Main thread, and it doesn't return any progress notification. I never needed .NET magic code to report progress automatically, I just needed a better solution than :

Action<String, String> exec = DoSomethingLongAndNotReturnAnyNotification;
IAsyncResult result = exec.BeginInvoke(path, parameters, null, null);
while (!result.IsCompleted)
{
  Application.DoEvents();
}
exec.EndInvoke(result);

使进度条保持活动状态(意味着不冻结但刷新标记)

that keeps the progress bar alive (means not freezing but refreshes the marque)

推荐答案

在我看来,你至少在一个错误的假设下运作.

在您的问题中,您这样说:

In your question you say this:

BackgroundWorker 不是答案因为这可能是我没有得到进度通知,这意味着不会有电话ProgressChanged 因为 DoWork 是一个对外部函数的单一调用...

BackgroundWorker is not the answer because it may be that I don't get the progress notification, which means there would be no call to ProgressChanged as the DoWork is a single call to an external function . . .

实际上,是否调用ProgressChanged事件并不重要.该事件的全部目的是将控制权暂时转移回 GUI 线程,以进行更新,以某种方式反映 BackgroundWorker 正在完成的工作的进度.如果您只是显示一个选取框进度条,那么引发 ProgressChanged 事件实际上毫无意义.只要显示进度条就会继续旋转,因为BackgroundWorker 正在与 GUI 不同的线程上工作.

Actually, it does not matter whether you call the ProgressChanged event or not. The whole purpose of that event is to temporarily transfer control back to the GUI thread to make an update that somehow reflects the progress of the work being done by the BackgroundWorker. If you are simply displaying a marquee progress bar, it would actually be pointless to raise the ProgressChanged event at all. The progress bar will continue rotating as long as it is displayed because the BackgroundWorker is doing its work on a separate thread from the GUI.

(附带说明,DoWork 是一个事件,这意味着它不只是对外部函数的一次调用";您可以添加任意多个处理程序随您喜欢;并且每个处理程序都可以包含任意数量的函数调用.)

(On a side note, DoWork is an event, which means that it is not just "a single call to an external function"; you can add as many handlers as you like; and each of those handlers can contain as many function calls as it likes.)

对我来说,听起来您认为更新 GUI 的唯一方法是调用 Application.DoEvents:

To me it sounds like you believe that the only way for the GUI to update is by calling Application.DoEvents:

我需要一直打电话给Application.DoEvents();为了进度条不断旋转.

I need to keep call the Application.DoEvents(); for the progress bar to keep rotating.

这在多线程场景中并非如此;如果您使用 BackgroundWorker,GUI 将继续响应(在其自己的线程上),而 BackgroundWorker 执行已附加到其 DoWork 事件.下面是一个简单的示例,说明这对您有何帮助.

This is not true in a multithreaded scenario; if you use a BackgroundWorker, the GUI will continue to be responsive (on its own thread) while the BackgroundWorker does whatever has been attached to its DoWork event. Below is a simple example of how this might work for you.

private void ShowProgressFormWhileBackgroundWorkerRuns() {
    // this is your presumably long-running method
    Action<string, string> exec = DoSomethingLongAndNotReturnAnyNotification;

    ProgressForm p = new ProgressForm(this);

    BackgroundWorker b = new BackgroundWorker();

    // set the worker to call your long-running method
    b.DoWork += (object sender, DoWorkEventArgs e) => {
        exec.Invoke(path, parameters);
    };

    // set the worker to close your progress form when it's completed
    b.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) => {
        if (p != null && p.Visible) p.Close();
    };

    // now actually show the form
    p.Show();

    // this only tells your BackgroundWorker to START working;
    // the current (i.e., GUI) thread will immediately continue,
    // which means your progress bar will update, the window
    // will continue firing button click events and all that
    // good stuff
    b.RunWorkerAsync();
}

3.不能在同一个线程上同时运行两个方法

你说:

我只需要打电话Application.DoEvents() 使Marque 进度条将起作用,而worker 函数在 Main 中工作线 ...

I just need to call Application.DoEvents() so that the Marque progress bar will work, while the worker function works in the Main thread . . .

您所要求的是完全不真实.Windows 窗体应用程序的主"线程是 GUI 线程,如果它忙于您的长时间运行的方法,则不会提供视觉更新.如果您不这么认为,我怀疑您误解了 BeginInvoke 的作用:它在单独的线程上启动一个委托.事实上,您在问题中包含的在 exec.BeginInvokeexec.EndInvoke 之间调用 Application.DoEvents 的示例代码是多余的;您实际上是从 GUI 线程重复调用 Application.DoEvents无论如何都会更新.(如果您发现其他情况,我怀疑是因为您立即调用了 exec.EndInvoke,这会阻塞当前线程,直到方法完成.)

What you're asking for is simply not real. The "main" thread for a Windows Forms application is the GUI thread, which, if it's busy with your long-running method, is not providing visual updates. If you believe otherwise, I suspect you misunderstand what BeginInvoke does: it launches a delegate on a separate thread. In fact, the example code you have included in your question to call Application.DoEvents between exec.BeginInvoke and exec.EndInvoke is redundant; you are actually calling Application.DoEvents repeatedly from the GUI thread, which would be updating anyway. (If you found otherwise, I suspect it's because you called exec.EndInvoke right away, which blocked the current thread until the method finished.)

是的,您正在寻找的答案是使用 BackgroundWorker.

So yes, the answer you're looking for is to use a BackgroundWorker.

可以使用BeginInvoke,而不是从GUI线程调用EndInvoke(如果方法没有完成,它将阻塞它),将 AsyncCallback 参数传递给您的 BeginInvoke 调用(而不是仅传递 null),然后关闭回调中的进度表.但是请注意,如果您这样做,您将不得不从 GUI 线程调用关闭进度表单的方法,否则您将尝试关闭一个表单,它是一个 GUI 函数,从非 GUI 线程.但实际上,使用 BeginInvoke/EndInvoke 的所有陷阱都已经你用 BackgroundWorker 类解决了,即使您认为它是.NET 魔术代码"(对我来说,它只是一个直观且有用的工具).

You could use BeginInvoke, but instead of calling EndInvoke from the GUI thread (which will block it if the method isn't finished), pass an AsyncCallback parameter to your BeginInvoke call (instead of just passing null), and close the progress form in your callback. Be aware, however, that if you do that, you're going to have to invoke the method that closes the progress form from the GUI thread, since otherwise you'll be trying to close a form, which is a GUI function, from a non-GUI thread. But really, all the pitfalls of using BeginInvoke/EndInvoke have already been dealt with for you with the BackgroundWorker class, even if you think it's ".NET magic code" (to me, it's just an intuitive and useful tool).

这篇关于在 C# 中做一些工作时显示进度条?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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