2010-07-21 4 views
13

Ho una app di Windows # in forma che ho buttato insieme. E 'abbastanza semplice: \C# aggiorna e aggiungi il valore della casella di testo utilizzando il processo di backgroundworker

ingressi:

  • stringa di testo
  • fonte percorso della cartella
  • percorso della cartella di destinazione
  • intero contano

Le ricerche app attraverso file di testo nella cartella di origine per la stringa di testo inserita; se trova la stringa, copia quel file e un file immagine con lo stesso nome nella cartella di destinazione. Lo fa comunque molte volte in base all'input intero.

Così ho un tasto, e in caso pulsante di scatto io chiamo

ProcessImages(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString()); 

che è:

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages) 
     {   
      int ImageCounter = 0; 
      int MaxImages = Convert.ToInt32(strNumImages); 

      DirectoryInfo di = new DirectoryInfo(SourceFolder); 

      foreach (FileInfo fi in di.GetFiles("*.txt")) 
      { 
       if (fi.OpenText().ReadToEnd().Contains(DID)) 
       { 
        //found one! 
        FileInfo fi2 = new FileInfo(fi.FullName.Replace(".txt", ".tif")); 
        if (fi2.Exists) 
        { 
         try 
         { 
          tbOutput.Text += "Copying " + fi2.FullName + " to " + tbDest.Text + "\r\n"; 
          fi2.CopyTo(tbDest.Text + @"\" + fi2.Name, true); 
          tbOutput.Text += "Copying " + fi.FullName + " to " + tbDest.Text + "\r\n"; 
          fi.CopyTo(tbDest.Text + @"\" + fi.Name, true); 

          ImageCounter++; 
         } 
         catch (Exception ex) 
         { 
          MessageBox.Show(ex.Message); 
         } 
        } 
       } 

       if (ImageCounter >= MaxImages) 
        break; 

      } 

     } 

Quello che succede è che il processo funziona bene, ma voglio aggiornare un casella di testo sul modulo con avanzamento mentre i file vengono copiati. Fondamentalmente il modulo si stacca mentre è in esecuzione e, al termine, l'output è nella casella di testo. Mi piacerebbe implementare un BackgroundWorker per farlo aggiornare l'interfaccia mentre è in esecuzione.

Ho esaminato gli esempi ma non li sto seguendo davvero. Non ho un valore percentuale completo, voglio solo aggiornarlo. Text cambia ogni iterazione e lo mostra. Non penso nemmeno di dover necessariamente inserire l'azione di copia effettiva in thread diversi, ma sembra proprio che debba essere eseguito separatamente dal thread dell'interfaccia utente principale. Forse ho finito per complicare tutto questo ... qualcuno può spingermi nella giusta direzione? Grazie!

risposta

7

Se si utilizza un lavoro in background, è possibile utilizzare il metodo ReportProgress per restituire qualsiasi numero intero, ad esempio il numero di record elaborati. Non deve essere una percentuale. Quindi, nel gestore ProgressChanged puoi aggiornare la tua casella di testo. Per esempio.

int count = e.ProgressPercentage; 
textBox1.Text = string.Format("{0} images processed.", count); 

Se non si desidera utilizzare un lavoratore in background è possibile chiamare Application.DoEvents() all'interno del ciclo. Ciò fornirà all'interfaccia utente l'opportunità di aggiornarsi e rispondere alle azioni dell'utente. Ma attenzione: rallenterà molto il tuo programma, quindi potresti chiamarlo solo ogni 100 iterazione.

2

L'interfaccia utente non si aggiorna perché non si consente l'elaborazione di alcun messaggio finestra nel ciclo di elaborazione file a esecuzione prolungata. Le app WinForms vengono ridisegnate in risposta ai messaggi WM_PAINT che vengono elaborati nella coda messaggi nel thread principale.

La soluzione più semplice è forzare un aggiornamento dell'interfaccia utente: provare a chiamare Update() sul modulo dopo aver modificato la casella di testo all'interno del ciclo.

L'app sarà ancora bloccata dall'utente (non rispondente ai clic del mouse, ecc.) Ma questo dovrebbe almeno richiamare i messaggi di avanzamento sullo schermo. Se l'aggiornamento del display è tutto ciò di cui hai veramente bisogno, fermati qui.

Il livello successivo di soluzione sarebbe consentire all'applicazione di elaborare i messaggi della finestra in sospeso nel ciclo di elaborazione dei file. Chiama Application.DoEvents() nel tuo loop (invece di form.Update). Ciò consentirà al modulo di ridisegnarsi con gli aggiornamenti di output del testo e di eliminare il blocco dell'interfaccia utente - l'app può rispondere alle attività del mouse e della tastiera.

Attenzione, tuttavia, l'utente potrebbe fare clic sul pulsante che ha avviato l'attività corrente mentre è in corso l'attività corrente, rientranza. Dovresti almeno disabilitare il menu o il pulsante che dà il via alla lunga elaborazione dei file per prevenire la ricorrenza.

Un terzo livello di soluzione consiste nell'utilizzare un thread in background per l'elaborazione dei file. Questo introduce tutta una serie di nuovi problemi di cui devi essere consapevole e in molti casi i thread sono eccessivi.Non ha molto senso spingere l'elaborazione dei file in un thread in background se non hai intenzione di permettere all'utente di fare nient'altro con la tua app mentre avviene l'elaborazione dei file.

+0

l'OP è già sulla strada giusta, che è quella di utilizzare BackgroundWorker, che sia crea il thread in background (beh, utilizza un pool thread) e tratta alcuni dei problemi a cui alludi nel tuo ultimo paragrafo. –

+0

L'OP non indica che l'utente ha nient'altro da fare nell'interfaccia utente mentre l'elaborazione dei file è in corso, quindi un thread in background potrebbe essere overkill IMO. – dthorpe

13

Sei sulla strada giusta con l'operatore in background. Ecco un esempio che ho messo insieme per mostrarti come farlo. Creare una nuova app di Windows con Form1. Aggiungi 4 controlli ad essa: label1, backgroundWorker1, button1 e button2. Quindi usa questo code-behind. Quindi è possibile utilizzare ReportProgress userState per riportare al thread principale ciò che si desidera. In questo esempio, sto passando una stringa. Il gestore eventi ProgressChanged si trova sul thread dell'interfaccia utente e aggiorna la casella di testo.

public partial class Form1 : Form 
{ 
    int backgroundInt; 
    public Form1() 
    { 
     InitializeComponent(); 
     backgroundWorker1.WorkerReportsProgress = true; 
    } 

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     label1.Text = e.UserState as string; 
    } 

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     backgroundInt = 1; 
     while (backgroundWorker1.CancellationPending == false) 
     { 
      System.Threading.Thread.Sleep(500); 
      backgroundWorker1.ReportProgress(0, 
       String.Format("I found file # {0}!", backgroundInt)); 
      backgroundInt++; 
     } 
    } 


    private void button1_Click(object sender, EventArgs e) 
    { 
     backgroundWorker1.RunWorkerAsync(); 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     backgroundWorker1.CancelAsync(); 
    } 
} 
+4

E il punto significativo qui in termini di reclamo originale "I do not have a percentage" dell'OP è che ReportProgress può prendere non solo la percentuale, ma * anche * un oggetto generico - che può (come in questo esempio) essere una stringa. –

+0

+1 per e.UserState come stringa. Non era affatto ovvio per me. – Jahmic

1

Basta creare un (1) un delegato per avvolgere la chiamata di metodo ProcessImages, (2) il fuoco al largo della chiamata utilizzando un delegato, e (3) quando si desidera aggiornare la casella di testo dal tuo metodo ProcessImages, Assegno per il funzionamento cross-thead e assicurarsi di fare l'aggiornamento dal thread principale:

delegate void ProcessImagesDelegate(string did, string sourceFolder, string destFolder, string strNumImages); 

private void ProcessImages(string DID, string SourceFolder, string DestFolder, string strNumImages) 
{ 
    // do work 

    // update textbox in form 
    if (this.textBox1.InvokeRequired) 
    { 
     this.textBox1.Invoke(new MethodInvoker(delegate() { this.textBox1.Text = "delegate update"; })); 
    } 
    else 
    { 
     this.textBox1.Text = "regular update"; 
    } 

    // do some more work 
} 

public void MyMethod() 
{ 
    new ProcessImagesDelegate(ProcessImages).BeginInvoke(tbDID.Text, tbSource.Text, tbDest.Text, comboBoxNumberImages.SelectedItem.ToString(), null, null); 
} 
+0

Perché è meglio di utilizzare BackgroundWorker? –

+0

Poiché entrambi usano il pool di thread e nessuno mi dà prestazioni migliori rispetto all'altro, tendo a preferire i delegati meglio dato che generalmente mi danno un codice più semplice/pulito (imho), in più questo è solo un altro modo per realizzare ciò che è vuole – Jason