C# 如何让进度条更新得足够快?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/977278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make the progress bar update fast enough?
提问by Malfist
I'm using a progress bar to show the user how far along the process is. It has 17 steps, and it can take anywhere from ~5 seconds to two or three minutes depending on the weather (well, database)
我正在使用进度条向用户显示过程进行了多远。它有 17 个步骤,根据天气可能需要大约 5 秒到两到三分钟(好吧,数据库)
I had no problem with this in XP, the progress bar went fine, but when testing it in vista I found that it is no longer the case.
我在 XP 中对此没有问题,进度条运行良好,但是在 vista 中测试时,我发现情况不再如此。
For example: if it takes closer to 5 seconds, it mightmake it a 1/3 of the way through before disappearing because it's completed. Even though it's progress is at 17 of 17, it doesn't show it. I believe this is because of the animation Vista imposes on progress bars and the animation cannot finish fast enough.
例如:如果它需要接近 5 秒,它可能会在消失之前使它通过 1/3,因为它已完成。即使它的进度是 17 中的 17,它也没有显示出来。我相信这是因为 Vista 对进度条施加了动画,并且动画无法足够快地完成。
Does anyone know how I can correct this?
有谁知道我该如何纠正这个问题?
Here is the code:
这是代码:
This is the part that updates the progress bar, waiting is the form that has the progress bar.
这是更新进度条的部分,wait是有进度条的表单。
int progress = 1;
//1 Cash Receipt Items
waiting.setProgress(progress, 18, progress, "Cash Receipt Items");
tblCashReceiptsApplyToTableAdapter1.Fill(rentalEaseDataSet1.tblCashReceiptsApplyTo);
progress++;
//2 Cash Receipts
waiting.setProgress(progress, "Cash Receipts");
tblCashReceiptsTableAdapter1.Fill(rentalEaseDataSet1.tblCashReceipts);
progress++;
//3 Checkbook Codes
waiting.setProgress(progress, "Checkbook Codes");
tblCheckbookCodeTableAdapter1.Fill(rentalEaseDataSet1.tblCheckbookCode);
progress++;
//4 Checkbook Entries
waiting.setProgress(progress, "Checkbook Entries");
tblCheckbookEntryTableAdapter1.Fill(rentalEaseDataSet1.tblCheckbookEntry);
progress++;
//5 Checkbooks
waiting.setProgress(progress, "Checkbooks");
tblCheckbookTableAdapter1.Fill(rentalEaseDataSet1.tblCheckbook);
progress++;
//6 Companies
waiting.setProgress(progress, "Companies");
tblCompanyTableAdapter1.Fill(rentalEaseDataSet1.tblCompany);
progress++;
//7 Expenses
waiting.setProgress(progress, "Expenses");
tblExpenseTableAdapter1.Fill(rentalEaseDataSet1.tblExpense);
progress++;
//8 Incomes
waiting.setProgress(progress, "Incomes");
tblIncomeTableAdapter1.Fill(rentalEaseDataSet1.tblIncome);
progress++;
//9 Properties
waiting.setProgress(progress, "Properties");
tblPropertyTableAdapter1.Fill(rentalEaseDataSet1.tblProperty);
progress++;
//10 Rental Units
waiting.setProgress(progress, "Rental Units");
tblRentalUnitTableAdapter1.Fill(rentalEaseDataSet1.tblRentalUnit);
progress++;
//11 Tenant Status Values
waiting.setProgress(progress, "Tenant Status Values");
tblTenantStatusTableAdapter1.Fill(rentalEaseDataSet1.tblTenantStatus);
progress++;
//12 Tenants
waiting.setProgress(progress, "Tenants");
tblTenantTableAdapter1.Fill(rentalEaseDataSet1.tblTenant);
progress++;
//13 Tenant Transaction Codes
waiting.setProgress(progress, "Tenant Transaction Codes");
tblTenantTransCodeTableAdapter1.Fill(rentalEaseDataSet1.tblTenantTransCode);
progress++;
//14 Transactions
waiting.setProgress(progress, "Transactions");
tblTransactionTableAdapter1.Fill(rentalEaseDataSet1.tblTransaction);
progress++;
//15 Vendors
waiting.setProgress(progress, "Vendors");
tblVendorTableAdapter1.Fill(rentalEaseDataSet1.tblVendor);
progress++;
//16 Work Order Categories
waiting.setProgress(progress, "Work Order Categories");
tblWorkOrderCategoryTableAdapter1.Fill(rentalEaseDataSet1.tblWorkOrderCategory);
progress++;
//17 Work Orders
waiting.setProgress(progress, "Work Orders");
tblWorkOrderTableAdapter1.Fill(rentalEaseDataSet1.tblWorkOrder);
progress++;
//18 Stored procs
waiting.setProgress(progress, "Stored Procedures");
getAllCheckbookBalancesTableAdapter1.Fill(rentalEaseDataSet1.GetAllCheckbookBalances);
getAllTenantBalancesTableAdapter1.Fill(rentalEaseDataSet1.GetAllTenantBalances);
//getCheckbookBalanceTableAdapter1;
//getTenantBalanceTableAdapter1;
getTenantStatusID_CurrentTableAdapter1.Fill(rentalEaseDataSet1.GetTenantStatusID_Current);
getTenantStatusID_FutureTableAdapter1.Fill(rentalEaseDataSet1.GetTenantStatusID_Future);
getTenantStatusID_PastTableAdapter1.Fill(rentalEaseDataSet1.GetTenantStatusID_Past);
selectVacantRentalUnitsByIDTableAdapter1.Fill(rentalEaseDataSet1.SelectVacantRentalUnitsByID);
getRentBasedBalancesTableAdapter1.Fill(rentalEaseDataSet1.GetRentBasedBalances);
getAgingBalanceTableAdapter2.Fill(rentalEaseDataSet1.GetAgingBalance);
waiting.Close();
Here is the waiting form:
这是等候表格:
public partial class PleaseWaitDialog : Form {
public PleaseWaitDialog() {
CheckForIllegalCrossThreadCalls = false;
InitializeComponent();
}
public void setProgress(int current, int max, int min, string loadItem) {
Debug.Assert(min <= max, "Minimum is bigger than the maximum!");
Debug.Assert(current >= min, "The current progress is less than the minimum progress!");
Debug.Assert(current <= max, "The progress is greater than the maximum progress!");
prgLoad.Minimum = min;
prgLoad.Maximum = max;
prgLoad.Value = current;
lblLoadItem.Text = loadItem;
}
public void setProgress(int current, string loadItem) {
this.setProgress(current, prgLoad.Maximum, prgLoad.Minimum, loadItem);
}
}
采纳答案by VVS
Try invoking the call to the waiting.setProgess()
method since waiting
seems to live in another thread and this would be a classic cross thread call(which the compiler warns you about if you let him).
尝试调用对该waiting.setProgess()
方法的调用,因为它waiting
似乎存在于另一个线程中,这将是一个经典的跨线程调用(如果你让他这样做,编译器会警告你)。
Since Control.Invoke
is a bit clumsy to use I usually use an extension method that allows me to pass a lambda expression:
由于Control.Invoke
使用起来有点笨拙,我通常使用允许我传递 lambda 表达式的扩展方法:
waiting.ThreadSafeInvoke(() => waiting.setProgress(...));
.
.
// also see http://stackoverflow.com/questions/788828/invoke-from-different-thread
public static class ControlExtension
{
public static void ThreadSafeInvoke(this Control control, MethodInvoker method)
{
if (control != null)
{
if (control.InvokeRequired)
{
control.Invoke(method);
}
else
{
method.Invoke();
}
}
}
}
回答by Marc Gravell
It soundslike you're doing everything on the UI thread and thus not releasing the message pump. Have you tried using smoething like BackgroundWorker
and the ProgressChanged
event? See MSDNfor an example.
这听起来像你正在做的UI线程上的所有内容,因此不释放消息泵。您是否尝试过使用 smoething likeBackgroundWorker
和ProgressChanged
事件?有关示例,请参阅MSDN。
BackgroundWorker
is ideal for loading external data - but note that you shouldn't do any data-binding etc until you get back to the UI thread (or just use Invoke
/BeginInvoke
to push work to the UI thread).
BackgroundWorker
非常适合加载外部数据 - 但请注意,在返回 UI 线程(或仅使用Invoke
/BeginInvoke
将工作推送到 UI 线程)之前,您不应进行任何数据绑定等操作。
回答by Brad Bruce
First. I'd never turn off the CheckForIllegalCrossThreadCalls option.
第一的。我永远不会关闭 CheckForIllegalCrossThreadCalls 选项。
Second. Add a Refresh() after you update the progress. Just because you're doing work in a different thread doesn't mean your GUI thread is going to get around to updating.
第二。更新进度后添加 Refresh()。仅仅因为您在不同的线程中工作并不意味着您的 GUI 线程将开始更新。
回答by VVS
I have the same problem. I have a form with multiple progress bars (top one is for example file x/n, bottom one is task y/m) The top progress bar does not update TIMELYwhile the bottom one does Programatically I update it, invalidate, explicit process message, refresh or sleep does not fix it. Funny thing is that bottom progress bar and other component (time elapsed text) updates fine. This is purely a Vista+theme problem (animations like previously suggested, XP or Vista with classic theme works fine. When displaying a message box after the top progress bar has travelled to 100 (programmatically, not visually) I first see the message box and then I see the progress completing
我也有同样的问题。我有一个带有多个进度条的表单(顶部一个是例如文件 x/n,底部一个是任务 y/m)顶部进度条不会及时更新,而底部一个以编程方式更新我更新它,使显式进程消息无效,刷新或睡眠不能修复它。有趣的是底部进度条和其他组件(经过时间的文本)更新得很好。这纯粹是一个 Vista+ 主题问题(像之前建议的那样,具有经典主题的 XP 或 Vista 的动画效果很好。在顶部进度条移动到 100(以编程方式,而不是在视觉上)后显示消息框时,我首先看到消息框和然后我看到进度完成
I found that SetWindowTheme(ProgressBar.Handle, ' ', ' '); as explained on Disabling progress bar animation on Vista Aeroworks (but I have old style progress bars now)
我发现 SetWindowTheme(ProgressBar.Handle, ' ', ' '); 如在 Vista Aero上禁用进度条动画所解释的那样 (但我现在有旧式进度条)
回答by Mark Lansdown
Vista introduced an animation effect when updating the progress bar - it tries to smoothly scroll from the previous position to the newly-set position, which creates a nasty time lag in the update of the control. The lag is most noticeable when you jump a progress bar in large increments, say from 25% to 50% in one jump.
Vista 在更新进度条时引入了动画效果——它试图从之前的位置平滑地滚动到新设置的位置,这会在控件的更新中产生令人讨厌的时间滞后。当您以较大的增量跳跃进度条时,滞后最为明显,例如一次跳跃从 25% 到 50%。
As another poster pointed out, you can disable the Vista theme for the progress bar and it will then mimic the behavior of XP progress bars.
正如另一位海报指出的那样,您可以禁用进度条的 Vista 主题,然后它会模仿 XP 进度条的行为。
I have found another workaround: if you set the progress bar backwards, it will immediately paint to this location. So, if you want to jump from 25% to 50%, you would use the (admittedly hackish) logic:
我找到了另一种解决方法:如果您将进度条向后设置,它会立即绘制到该位置。所以,如果你想从 25% 跳到 50%,你会使用(不可否认的hackish)逻辑:
progressbar.Value = 50;
progressbar.Value = 49;
progressbar.Value = 50;
I know, I know - it's a silly hack - but it does work!
我知道,我知道 - 这是一个愚蠢的黑客 - 但它确实有效!
回答by Silas Hansen
The reason for this whole mess is the interpolating animation effect introduced by Vista and W7. It has aboslutely nothing to do with thread blocking issues. Calling setProgress() or setting the Value property driectly, triggers an animation effect to occur, which I will explain how to cheat:
造成这一切的原因是 Vista 和 W7 引入的插值动画效果。它与线程阻塞问题完全无关。调用 setProgress() 或者直接设置 Value 属性,触发动画效果发生,我将解释如何作弊:
I came up with a hack of setting the maximum according to a fixed value. The maximum property does not trigger the effect, thus you get to freely move the progress around with instant response.
我想出了一个根据固定值设置最大值的技巧。最大属性不会触发效果,因此您可以通过即时响应自由移动进度。
Remember that the actual shown progress is given by: ProgressBar.Value / ProgressBar.Maximum. With this in mind, the example below will move the progress from 0 to 100, repensented by i:
请记住,实际显示的进度由:ProgressBar.Value / ProgressBar.Maximum 给出。考虑到这一点,下面的示例会将进度从 0 移动到 100,由 i 表示:
ProgressBar works like this:
progress = value / maximum
therefore:
maximum = value / progress
I added some scaling factors needed, should be self explanatory:
我添加了一些需要的缩放因子,应该是不言自明的:
progressBar1.Maximum *= 100;
progressBar1.Value = progressBar1.Maximum / 100;
for (int i = 1; i < 100; i++)
{
progressBar1.Maximum = (int)((double)progressBar1.Value / (double)(i + 1) * 100);
Thread.Sleep(20);
}
回答by deegee
I use Mark Lansdown's excellent answer as an Extension method for the ProgressBar control.
我使用 Mark Lansdown 的优秀答案作为 ProgressBar 控件的扩展方法。
public static void ValueFast(this ProgressBar progressBar, int value)
{
progressBar.Value = value;
if (value > 0) // prevent ArgumentException error on value = 0
{
progressBar.Value = value - 1;
progressBar.Value = value;
}
}
Or you can also do it this way which only sets the ProgressBar value property twice instead of three times:
或者你也可以这样做,只设置 ProgressBar 值属性两次而不是三次:
public static void ValueFast(this ProgressBar progressBar, int value)
{
if (value < 100) // prevent ArgumentException error on value = 100
{
progressBar.Value = value + 1; // set the value +1
}
progressBar.Value = value; // set the actual value
}
Simply call it on any ProgressBar control using the extension method:
只需使用扩展方法在任何 ProgressBar 控件上调用它:
this.progressBar.ValueFast(50);
If you really wanted to you could also check the current Windows Environment and only do the hack section of code for Windows Vista+ since Windows XP's ProgressBar does not have the slow progress animation.
如果您真的想要,您还可以检查当前的 Windows 环境,并且只为 Windows Vista+ 执行 hack 部分的代码,因为 Windows XP 的 ProgressBar 没有缓慢的进度动画。
回答by Nyerguds
Expanding on the answer given by Silas Hansen, this one seems to give me perfect results every time.
扩展 Silas Hansen 给出的答案,这个似乎每次都能给我完美的结果。
protected void UpdateProgressBar(ProgressBar prb, Int64 value, Int64 max)
{
if (max < 1)
max = 1;
if (value > max)
value = max;
Int32 finalmax = 1;
Int32 finalvalue = 0;
if (value > 0)
{
if (max > 0x8000)
{
// to avoid overflow when max*max exceeds Int32.MaxValue.
// 0x8000 is a safe value a bit below the actual square root of Int32.MaxValue
Int64 progressDivideValue = 1;
while ((max / progressDivideValue) > 0x8000)
progressDivideValue *= 0x10;
finalmax = (Int32)(max / progressDivideValue);
finalvalue = (Int32)(value / progressDivideValue);
}
else
{
// Upscale values to increase precision, since this is all integer division
// Again, this can never exceed 0x8000.
Int64 progressMultiplyValue = 1;
while ((max * progressMultiplyValue) < 0x800)
progressMultiplyValue *= 0x10;
finalmax = (Int32)(max * progressMultiplyValue);
finalvalue = (Int32)(value * progressMultiplyValue);
}
}
if (finalvalue <= 0)
{
prb.Maximum = (Int32)Math.Min(Int32.MaxValue, max);
prb.Value = 0;
}
else
{
// hacky mess, but it works...
// Will pretty much empty the bar for a split second, but this is normally never visible.
prb.Maximum = finalmax * finalmax;
// Makes sure the value will DEcrease in the last operation, to ensure the animation is skipped.
prb.Value = Math.Min(prb.Maximum, (finalmax + 1));
// Sets the final values.
prb.Maximum = (finalmax * finalmax) / finalvalue;
prb.Value = finalmax;
}
}
回答by AdvanTiSS
Did you try Application.DoEvents();?
你有没有试过Application.DoEvents(); ?