9

我正在使用进度条向用户显示该过程的进度。它有 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();

这是等待表格:

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

9 回答 9

37

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

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

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

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

我知道,我知道 - 这是一个愚蠢的 hack - 但它确实有效!

于 2009-07-31T18:35:48.460 回答
11

造成这一切的原因是 Vista 和 W7 引入的插值动画效果。它与线程阻塞问题绝对无关。直接调用 setProgress() 或设置 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);
}
于 2009-11-24T09:09:04.900 回答
3

听起来您正在 UI 线程上做所有事情,因此没有释放消息泵。您是否尝试过使用 smoething likeBackgroundWorkerProgressChanged事件?有关示例,请参见MSDN 。

BackgroundWorkerInvoke非常适合加载外部数据 - 但请注意,在您返回 UI 线程(或仅使用/BeginInvoke将工作推送到 UI 线程)之前,您不应进行任何数据绑定等。

于 2009-06-10T18:14:09.973 回答
2

尝试调用对该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();
            }
        }
    }
}
于 2009-06-10T19:34:19.903 回答
1

我使用 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;
    }

}

或者您也可以这样做,只设置 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+ 执行 hack 部分的代码,因为 Windows XP 的 ProgressBar 没有缓慢的进度动画。

于 2013-12-17T17:54:52.080 回答
1

扩展 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;
    }
}
于 2015-08-07T07:51:14.910 回答
0

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

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

于 2009-06-10T19:07:41.220 回答
0

我也有同样的问题。我有一个带有多个进度条的表单(顶部是例如文件 x/n,底部是任务 y/m)顶部进度条不会及时更新,而底部进度条会以编程方式更新它,使其无效,显式处理消息,刷新或睡眠不能解决它。有趣的是底部进度条和其他组件(经过时间的文本)更新正常。这纯粹是一个 Vista + 主题问题(之前建议的动画,XP 或具有经典主题的 Vista 工作正常。在顶部进度条移动到 100 后显示消息框时(以编程方式,而不是视觉方式)我首先看到消息框,然后然后我看到进度完成

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

于 2009-06-26T14:09:21.480 回答
0

你试过Application.DoEvents(); ?

于 2015-11-12T12:15:00.253 回答