如何让进度条更新得足够快?

我正在使用进度条向用户显示该过程的进度。 它有17个步骤,根据天气情况可能需要大约5秒到2到3分钟(好吧,数据库)

我在XP中没有遇到任何问题,进度条运行良好,但是在vista中进行测试时我发现它不再是这种情况。

例如:如果它接近5秒,它可能会在消失之前使其成为1/3,因为它已经完成。 即使它的进展是17的17,但它没有显示出来。 我相信这是因为动画Vista强加于进度条并且动画无法快速完成。

有谁知道我怎么纠正这个?

这是代码:

这是更新进度条的部分,等待是具有进度条的表单。

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(); 

这是等待forms:

 public partial class PleaseWaitDialog : Form { public PleaseWaitDialog() { CheckForIllegalCrossThreadCalls = false; InitializeComponent(); } public void setProgress(int current, int max, int min, string loadItem) { Debug.Assert(min = 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); } } 

尝试调用waiting.setProgess()方法,因为waiting似乎存在于另一个线程中,这将是一个经典的跨线程调用 (如果你让他,编译器会警告你)。

由于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(); } } } } 

Vista在更新进度条时引入了动画效果 – 它尝试从前一个位置平滑地滚动到新设置的位置,这在控件的更新中产生了令人讨厌的时滞。 当您以较大的增量跳跃进度条时,滞后最明显,例如在一次跳跃中从25%跳到50%。

正如另一张海报所指出的,您可以禁用进度条的Vista主题,然后它将模仿XP进度条的行为。

我找到了另一种解决方法:如果你向后设置进度条,它会立即绘制到这个位置。 所以,如果你想从25%跳到50%,你会使用(诚然是hackish)逻辑:

 progressbar.Value = 50; progressbar.Value = 49; progressbar.Value = 50; 

我知道,我知道 – 这是一个愚蠢的黑客 – 但它确实有效!

这整个混乱的原因是Vista和W7引入的插值动画效果。 它与线程阻塞问题毫无关系。 调用setProgress()或driectly设置Value属性会触发动画效果,我将解释如何作弊:

我想出了一个根据固定值设置最大值的黑客。 最大属性不会触发效果,因此您可以通过即时响应自由移动进度。

请记住,实际显示的进度由下式给出:ProgressBar.Value / ProgressBar.Maximum。 考虑到这一点,下面的例子将把进度从0移到100,由i重新表示:

 ProgressBar works like this: progress = value / maximum therefore: maximum = value / progress 

我添加了一些所需的缩放因子,应该是自我解释的:

 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); } 

听起来你正在UI线程上做所有事情,因此不会释放消息泵。 您是否尝试过像BackgroundWorkerProgressChanged事件一样使用smoething? 有关示例,请参阅MSDN 。

BackgroundWorker非常适合加载外部数据 – 但请注意,在返回UI线程之前不应该执行任何数据绑定等(或者只使用Invoke / BeginInvoke将工作推送到UI线程)。

我使用Mark Lansdown的优秀答案作为ProgressBar控件的Extension方法。

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

或者您也可以这样做,只将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 } 

只需使用扩展方法在任何ProgressBar控件上调用它:

 this.progressBar.ValueFast(50); 

如果您真的想要也可以检查当前的Windows环境,并且只执行Windows Vista +代码的黑客部分,因为Windows XP的ProgressBar没有慢进度动画。

扩大了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; } } 

第一。 我永远不会关闭CheckForIllegalCrossThreadCalls选项。

第二。 更新进度后添加Refresh()。 仅仅因为你在不同的线程中工作并不意味着你的GUI线程将会更新。

我也有同样的问题。 我有一个带有多个进度条的表单(顶部一个是例如文件x / n,底部一个是任务y / m)顶部进度条没有TIMELY更新而底部一个做了Programatically我更新它,invalidate,显示进程消息,刷新或睡眠不能解决它。 有趣的是底部进度条和其他组件(时间流逝文本)更新正常。 这纯粹是一个Vista +主题问题(像以前建议的动画,XP或Vista经典主题的工作正常。当在顶部进度条移动到100(编程,不可视)后显示一个消息框时,我首先看到消息框和然后我看到完成的进度

我发现了SetWindowTheme(ProgressBar.Handle,”,”); 正如在Vista Aero上禁用进度条动画所解释的那样(但我现在有旧样式进度条)

你尝试过Application.DoEvents(); ?