2009-06-10 2 views
8

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

У меня не было проблем с этим в XP, индикатор прогресса прошел нормально, но при тестировании я понял, что это уже не так.

Например: если требуется более 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); 
    } 
} 
+1

CheckForIllegalCrossThreadCalls = false <- возможно, вы должны поступать правильно, чтобы предотвратить эту ошибку (Invocation) :) – VVS

+0

Не имеет значения. – Malfist

+0

Итак, компилятор не предупреждает о перекрестных вызовах? – VVS

ответ

2

Попробуйте вызвать метод waiting.setProgess(), так как waiting, похоже, живет в другом потоке, и это будет classic cross thread call (о ком вы предупреждаете, если вы его позволите).

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

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(); 
      } 
     } 
    } 
} 
+0

Это не имело никакого значения, но спасибо за то, что показал мне эту картину. – Malfist

+2

Это не должно быть отмечено как ответ, так как оно не меняет медленную анимацию ProgressBar на Vista +. – deegee

3

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

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

+0

Он обновляется из делегата, созданного другой формой. – Malfist

+0

Но это на другую тему? –

+0

У меня создалось впечатление, что с помощью метода MethodInvoke создан новый поток. – Malfist

0

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

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

+0

Я уверен, что поток GUI обновляется, если он не заблокирован. – VVS

+0

Вызов обновления не сделал ничего. – Malfist

0

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

Я обнаружил, что SetWindowTheme (ProgressBar.Handle, '', ''); как объяснено на Disabling progress bar animation on Vista Aero работ (но у меня есть старые бруски прогресса стиля сейчас)

31

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

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

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

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

Я знаю, я знаю - это глупо рубить - но это работает!

+0

Да, это работает. Благодаря! Это мой глаз в моем коде, и я надеюсь, что MS решит эту проблему. – Han

+0

Ничего себе. Я потратил, вероятно, 2 часа на работу над моими потоками и обеспечил достаточное время сна для перерисовки пользовательского интерфейса, и ничего не получилось. Я добавил этот маленький взлом, и теперь индикатор выполнения обновляется чисто. Это очень грязный хак, но он просто работает. – drharris

+0

Спасибо !!! Это лучший (хотя и грязный) хак, который я видел, чтобы исправить эту глупую «особенность» –

9

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

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

Помните, что фактический показанный прогресс задается: ProgressBar.Value/ProgressBar.Maximum. Имея это в виду, на примере ниже будет двигаться прогресс от 0 до 100, repensented на г:

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

Brilliant. Это должен быть принятый ответ. – Nate

+0

Я знаю .. Но, к сожалению, мне кажется, что сначала может показаться сложным, чтобы люди могли попробовать попробовать ... –

+3

boooh ... Я из будущего ... Это лучшее решение. Сначала я сделал весь прогресс. progressbar.Value ++; вещь. Это работает намного лучше, если вы сделаете это. –

1

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

+0

Если индикатор выполнения используется в разных случаях для обработки списков элементов различной длины, обычно лучше адаптировать максимум к длине списка, что означает, что вы должны изменить свой " 100 'to progressBar.Maximum'. – Nyerguds

1

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

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

С тех пор я расширил это в [полный класс для обработки обновлений индикаторов выполнения] (http://nyerguds.arsaneus-design.com/project_stuff/nyertools/ProgressBarUpdater.cs). Это использует делегаты для обновления пользовательского интерфейса (как следует), и Thread.Sleep был удален. – Nyerguds

Смежные вопросы