Как я могу сделать обновление индикатора выполнения достаточно быстрым? - PullRequest
8 голосов
/ 10 июня 2009

Я использую индикатор выполнения, чтобы показать пользователю, как далеко продвинулся процесс. Он имеет 17 шагов, и это может занять от ~ 5 секунд до двух или трех минут в зависимости от погоды (ну, база данных)

У меня не было проблем с этим в 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);
    }
}

Ответы [ 9 ]

33 голосов
/ 31 июля 2009

Vista вводит анимационный эффект при обновлении индикатора выполнения - она ​​пытается плавно прокрутить от предыдущей позиции до новой установленной позиции, что создает неприятную задержку в обновлении элемента управления. Задержка наиболее заметна, когда вы прыгаете на индикатор выполнения с большими приращениями, скажем, от 25% до 50% за один прыжок.

Как указал другой автор, вы можете отключить тему Vista для индикатора выполнения, и тогда она будет имитировать поведение индикаторов выполнения XP.

Я нашел другой обходной путь: если вы установите индикатор выполнения в обратном направлении, он немедленно закрасит это место. Итак, если вы хотите подскочить с 25% до 50%, вы должны использовать (по общему признанию хакерскую) логику:

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

Я знаю, я знаю - это глупый взлом - но это работает!

9 голосов
/ 24 ноября 2009

Причиной всего этого беспорядка является интерполяционный эффект анимации, представленный в Vista и W7. Это абсолютно не связано с проблемами блокировки потоков. Вызов метода setProgress () или установка свойства Value непосредственно, запускает эффект анимации, который я объясню, как обманывать:

Я придумал, как настроить максимум в соответствии с фиксированным значением. Свойство Maximum не вызывает эффекта, поэтому вы можете свободно перемещать прогресс с мгновенным ответом.

Помните, что фактический показанный прогресс определяется как: 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);
}
3 голосов
/ 10 июня 2009

Это звучит , как будто вы все делаете в потоке пользовательского интерфейса и, таким образом, не отпускаете насос сообщений. Вы пробовали использовать сглаживание как BackgroundWorker и событие ProgressChanged? См. MSDN для примера.

BackgroundWorker идеально подходит для загрузки внешних данных - но учтите, что вы не должны выполнять привязку данных и т. Д., Пока не вернетесь к потоку пользовательского интерфейса (или просто не используйте Invoke / BeginInvoke, чтобы перенести работу на Пользовательский интерфейс).

2 голосов
/ 10 июня 2009

Попробуйте вызвать вызов метода waiting.setProgess(), так как waiting, кажется, живет в другом потоке, и это будет классический перекрестный вызов (о котором компилятор предупредит вас, если вы позволите ему) ,

Поскольку Control.Invoke немного неудобно в использовании, я обычно использую метод расширения, который позволяет мне передавать лямбда-выражение:

waiting.ThreadSafeInvoke(() => waiting.setProgress(...));

.

// also see /557470/vyzyvat-iz-drugoi-temy
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();
            }
        }
    }
}
1 голос
/ 07 августа 2015

Расширяя ответ, данный Сайласом Хансеном, этот, кажется, каждый раз дает мне идеальные результаты.

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;
    }
}
1 голос
/ 17 декабря 2013

Я использую отличный ответ Марка Лэнсдауна в качестве метода расширения для элемента управления 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 +, поскольку в ProgressBar в Windows XP отсутствует анимация медленного прогресса.

0 голосов
/ 12 ноября 2015

Вы пробовали Application.DoEvents (); ?

0 голосов
/ 26 июня 2009

У меня такая же проблема. У меня есть форма с несколькими индикаторами выполнения (верхняя часть - это, например, файл x / n, нижняя - это задача y / m) Верхний индикатор выполнения не обновляет TIMELY , а нижний - Программно я его обновляю, делаю недействительным, явно обрабатываю сообщение, обновляю или сплю не исправляю. Забавно, что нижний индикатор выполнения и другой компонент (прошедший время текст) обновляются нормально. Это просто проблема с темой Vista + (анимации, подобные ранее предложенным, XP или Vista с классической темой работают нормально. При отображении окна сообщения после того, как верхний индикатор выполнения перешел на 100 (программно, а не визуально), я сначала вижу окно сообщения, а затем вижу прогресс, завершающий

Я обнаружил, что SetWindowTheme (ProgressBar.Handle, '', ''); как объяснено на Отключение анимации индикатора выполнения в Vista Aero работает (но у меня теперь есть индикаторы в старом стиле)

0 голосов
/ 10 июня 2009

Во-первых. Я бы никогда не отключил опцию CheckForIllegalCrossThreadCalls.

Во-вторых. Добавьте Refresh () после обновления прогресса. То, что вы выполняете работу в другом потоке, не означает, что ваш поток с графическим интерфейсом будет обновляться.

...