簡體   English   中英

如何讓進度條更新得足夠快?

[英]How can I make the progress bar update fast enough?

我正在使用進度條向用戶顯示過程進行了多遠。 它有 17 個步驟,根據天氣可能需要大約 5 秒到兩到三分鍾(好吧,數據庫)

我在 XP 中對此沒有問題,進度條運行良好,但是在 vista 中測試時,我發現情況不再如此。

例如:如果它需要接近 5 秒,它可能會在消失之前使它通過 1/3,因為它已完成。 即使它的進度是 17 中的 17,它也沒有顯示出來。 我相信這是因為 Vista 對進度條施加了動畫,並且動畫無法足夠快地完成。

有誰知道我該如何糾正這個問題?

這是代碼:

這是更新進度條的部分,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();

這是等候表格:

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

Vista 在更新進度條時引入了動畫效果——它試圖從之前的位置平滑地滾動到新設置的位置,這會在控件的更新中產生令人討厭的時間滯后。 當您以較大的增量跳躍進度條時,滯后最為明顯,例如一次跳躍從 25% 到 50%。

正如另一位海報指出的那樣,您可以禁用進度條的 Vista 主題,然后它會模仿 XP 進度條的行為。

我找到了另一種解決方法:如果您將進度條向后設置,它會立即繪制到該位置。 所以,如果你想從 25% 跳到 50%,你會使用(無可否認的hackish)邏輯:

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

我知道,我知道 - 這是一個愚蠢的黑客 - 但它確實有效!

造成這一切的原因是 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);
}

聽起來你在 UI 線程上做所有事情,因此沒有釋放消息泵。 您是否嘗試過使用像BackgroundWorkerProgressChanged事件這樣的平滑處理? 有關示例,請參閱MSDN

BackgroundWorker非常適合加載外部數據 - 但請注意,在返回 UI 線程(或僅使用Invoke / BeginInvoke將工作推送到 UI 線程之前,您不應進行任何數據綁定等操作)。

嘗試調用對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();
            }
        }
    }
}

我使用 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 沒有緩慢的進度動畫。

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

我也有同樣的問題。 我有一個帶有多個進度條的表單(頂部一個是例如文件 x/n,底部一個是任務 y/m)頂部進度條不會及時更新,而底部一個以編程方式更新我更新它,使顯式進程消息無效,刷新或睡眠不能修復它。 有趣的是底部進度條和其他組件(經過時間的文本)更新得很好。 這純粹是一個 Vista+ 主題問題(以前建議的動畫,具有經典主題的 XP 或 Vista 工作正常。在頂部進度條移動到 100(以編程方式,而不是在視覺上)后顯示消息框時,我首先看到消息框和然后我看到進度完成

我發現 SetWindowTheme(ProgressBar.Handle, ' ', ' '); 在 Vista Aero禁用進度條動畫所解釋的那樣(但我現在有舊式進度條)

你有沒有試過Application.DoEvents(); ?

第一的。 我永遠不會關閉 CheckForIllegalCrossThreadCalls 選項。

第二。 更新進度后添加 Refresh()。 僅僅因為您在不同的線程中工作並不意味着您的 GUI 線程將開始更新。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM