2009-06-10 10 views
8

Sto usando una barra di avanzamento per mostrare all'utente quanto è lungo il processo. Ha 17 passaggi, e può richiedere da ~ 5 secondi a due o tre minuti a seconda del tempo (bene, database)Come posso aggiornare la barra di avanzamento abbastanza velocemente?

Non ho avuto problemi con questo in XP, la barra di avanzamento è andata bene, ma quando si esegue il test in vista ho scoperto che non è più il caso.

Ad esempio: se impiega più vicino a 5 secondi, è possibile che ne faccia un terzo prima di scomparire perché completato. Anche se il progresso è a 17 di 17, non lo mostra. Credo che questo sia dovuto all'animazione che Vista impone sulle barre di avanzamento e l'animazione non può finire abbastanza velocemente.

Qualcuno sa come posso correggere questo?

Ecco il codice:

Questa è la parte che aggiorna la barra di avanzamento, di attesa è la forma che ha la barra di avanzamento.

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

Qui è la forma d'attesa:

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 = falso <- Forse dovresti fare la cosa giusta per prevenire quell'errore (Invocazione) :) – VVS

+0

Non ha fatto differenza. – Malfist

+0

Quindi, il compilatore non ti avvisa delle chiamate incrociate? – VVS

risposta

2

Prova a richiamare la chiamata al metodo waiting.setProgess() poiché waiting sembra vivere in un altro thread e questo sarebbe un classic cross thread call (che il compilatore ti avvisa se glielo permetti).

Dal Control.Invoke è un po 'goffo di utilizzare io di solito uso un metodo di estensione che mi permette di passare un'espressione 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(); 
      } 
     } 
    } 
} 
+0

Questo non ha fatto differenza ma grazie per avermi mostrato quel modello. – Malfist

+2

Questo non dovrebbe essere contrassegnato come risposta poiché non modifica l'animazione ProgressBar lenta su Vista +. – deegee

3

E suona come si sta facendo tutto sul thread dell'interfaccia utente e quindi non rilasciare la pompa messaggio. Hai provato a utilizzare smoething come BackgroundWorker e l'evento ProgressChanged? Vedi MSDN per un esempio.

BackgroundWorker è l'ideale per il caricamento dei dati esterni - ma nota che non si deve fare alcuna associazione dati ecc fino a tornare al thread UI (o semplicemente usare Invoke/BeginInvoke per spingere lavoro al thread UI).

+0

Viene aggiornato da un delegato creato da un altro modulo. – Malfist

+0

Ma è su un thread diverso? –

+0

Ho avuto l'impressione che l'utilizzo di MethodInvoke abbia creato un nuovo thread. – Malfist

0

Primo. Non spegnerei mai l'opzione CheckForIllegalCrossThreadCalls.

Secondo. Aggiungi un aggiornamento() dopo aver aggiornato lo stato di avanzamento. Solo perché stai lavorando in un thread diverso non significa che il tuo thread della GUI andrà in giro per l'aggiornamento.

+0

Sono abbastanza sicuro che il thread della GUI si aggiorni se non è bloccato. – VVS

+0

La chiamata all'aggiornamento non ha fatto nulla. – Malfist

0

Ho lo stesso problema. Ho un modulo con più barre di avanzamento (il primo è ad esempio file x/n, in basso è compito y/m) La barra di progresso in alto non aggiorna TIMELY mentre quella in basso fa L'aggiornamento periodicamente, invalida, esplicita il messaggio di processo, aggiorna o non aggiorna il sonno. La cosa divertente è che la barra di avanzamento in basso e l'altro componente (tempo trascorso testo) si aggiorna bene. Questo è puramente un problema con il tema Vista + (le animazioni come suggerito in precedenza, XP o Vista con il tema classico funzionano bene. Quando si visualizza una finestra di messaggio dopo che la barra di avanzamento superiore è passata a 100 (programmaticamente, non visivamente) prima vedo la finestra di messaggio e poi vedo i progressi completando

Ho trovato che SetWindowTheme (ProgressBar.Handle, '', ''); come spiegato a Disabling progress bar animation on Vista Aero opere (ma devo vecchie barre di avanzamento stile ora)

31

Vista ha introdotto un effetto di animazione durante l'aggiornamento la barra di avanzamento - cerca di scorrere agevolmente dalla posizione precedente alla nuova carica-set, che crea un brutto intervallo di tempo nell'aggiornamento del controllo. Il ritardo è più evidente quando si salta una barra di avanzamento a grandi incrementi, ad esempio dal 25% al ​​50% in un salto.

Come un altro poster ha sottolineato, è possibile disabilitare il tema Vista per la barra di avanzamento e simulerà quindi il comportamento delle barre di avanzamento XP.

Ho trovato un'altra soluzione: se si imposta la barra di avanzamento all'indietro, verrà immediatamente dipinta in questa posizione. Quindi, se si desidera passare dal 25% al ​​50%, si può usare il (certamente hacker) logica:

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

Lo so, lo so - è un hack stupido - ma funziona!

+0

Sì, questo funziona. Grazie! È un pugno nel mio codice, e spero che MS risolverà questo problema. – Han

+0

Wow. Ho speso probabilmente 2 ore di lavoro sul mio threading e assicurandomi che avessi abbastanza tempo per dormire per il ridisegno dell'interfaccia utente, e niente ha funzionato. Ho aggiunto questo piccolo trucco e ora la barra di avanzamento si aggiorna in modo pulito. È un trucco molto sporco, ma funziona semplicemente. – drharris

+0

Grazie !!! Questo è il migliore (anche se sporco) hack che ho visto per sistemare questa stupida "Feature" –

9

Il motivo di tutto questo pasticcio è l'effetto di animazione interpolante introdotto da Vista e W7. Non ha assolutamente nulla a che fare con i problemi di blocco dei thread. Chiamando setProgress() o impostando la proprietà Value, si attiva un effetto di animazione, che spiegherò come imbrogliare:

Mi è venuto in mente un trucco per impostare il massimo in base a un valore fisso. La proprietà massima non attiva l'effetto, quindi puoi spostare liberamente il progresso intorno con una risposta immediata.

Ricordare che il progresso effettivo mostrato è dato da: ProgressBar.Value/ProgressBar.Maximum. Con questo in mente, l'esempio di seguito si sposterà lo stato di avanzamento da 0 a 100, repensented da i:

ProgressBar works like this: 
progress = value/maximum 

therefore: 
maximum = value/progress 

ho aggiunto alcuni fattori di scala necessarie, dovrebbe essere autoesplicativo:

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

Brillante. Questa dovrebbe essere la risposta accettata. – Nate

+0

Lo so .. Ma sfortunatamente, penso che potrebbe sembrare complicato in un primo momento per le persone prendere in considerazione l'idea di provarlo ... –

+3

boooh ... Io vengo dal futuro ... Questa è la soluzione migliore. Ho prima fatto tutto il progressbar.Value--; progressbar.Value ++; cosa. Questo funziona molto meglio se fai un tentativo. –

1

Io uso Mark Ottima risposta di Lansdown come metodo di estensione per il controllo 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; 
    } 

} 

O anche si può fare in questo modo che imposta solo la proprietà valore ProgressBar due volte invece di tre volte:

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 

} 

chiamano semplicemente su qualsiasi controllo ProgressBar utilizzando il metodo di estensione:

this.progressBar.ValueFast(50); 

Se si voleva davvero si potrebbe anche controllare l'attuale ambiente di Windows e solo fare la sezione di codice di hack per Windows Vista + dal ProgressBar di Windows XP non ha l'animazione del lento progresso.

+0

Se la barra di avanzamento viene utilizzata in diverse occasioni per elaborare elenchi di articoli con lunghezze diverse, è solitamente meglio adattare il massimo alla lunghezza della lista, il che significa che dovresti cambiare il tuo ' 100 'a 'progressBar.Maximum'. – Nyerguds

1

Espansione della risposta data da Silas Hansen, questo sembra darmi risultati perfetti ogni volta.

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

Da allora l'ho espanso in [una classe completa per la gestione degli aggiornamenti della barra di avanzamento] (http://nyerguds.arsaneus-design.com/project_stuff/nyertools/ProgressBarUpdater.cs). Questo utilizza i delegati per aggiornare l'interfaccia utente (come dovrebbe) e il Thread.Sleep è stato rimosso. – Nyerguds

Problemi correlati