2012-12-05 14 views
5

Sto scrivendo una classe che esegue una determinata operazione in una libreria. Ma l'operazione è noiosa, e voglio essere in grado di scoprire l'avanzamento del metodo all'interno di quella classe in modo che possa usarlo in un'applicazione WinForms per segnalare i progressi.Scrittura di un metodo che riporta i progressi

Sto pianificando di eseguire la mia classe su un altro thread nella mia applicazione WinForms e voglio che la classe sia separata dalle preoccupazioni dell'applicazione WinForms, e non voglio legarla a qualcosa di specifico diverso da quello lo fa.

Quale sarebbe il modo migliore per implementare un meccanismo di segnalazione dei progressi in una classe di biblioteca?

Sarebbe una buona idea avere in qualche modo una variabile di avanzamento nella classe e aggiungere un listener di eventi ad esso nella mia applicazione WinForms? E se lo è, come posso farlo?

Edit: Ho usato la classe BackgroundWorker prima, ma il mio problema è che non voglio che la mia classe di libreria da occupa una delle operazioni multithreading. Quindi non voglio invocare ReportProgress nella classe della libreria, voglio (forse) avere una variabile nella classe che contiene il progresso corrente e voglio che il thread dell'interfaccia utente in qualche modo "iscriva" ad esso. Non so se sia un buon modo di progettarlo però.

+0

Ci sono molti approcci diversi a questo, ma come si presenta il tuo codice esistente ..? sarebbe di grande aiuto se fornissi quello che hai provato fino ad ora .. – MethodMan

+0

Al momento non ho molto codice. Volevo scoprire come procedere prima di scrivere la lezione. Lo apprezzerei molto se potessi indicarmi alcuni noti approcci a questo problema. – hattenn

risposta

4

Cerca nella classe BackgroundWorker. Supporta il marshalling automatico tra thread per la segnalazione dei progressi e ha un modello di eventi molto semplice per questo tipo di supporto.

Edit: Data la sua posizione sull'uso direttamente BackgroundWorker, quello che si potrebbe fare è creare un semplice involucro:

// Error checking elided for expository purposes. 
public interface IProgressReporter 
{ 
    void ReportProgress(int progress, object status); 
} 

public class BackgroundWorkerProgressReporter : IProgressReporter 
{ 
    private BackgroundWorker _worker; 
    public BackgroundWorkerProgressReporter(BackgroundWorker worker) 
    { 
     _worker = worker; 
    } 

    public void ReportProgress(int progress, object status) 
    { 
     _worker.ReportProgress(progress, status); 
    } 
} 

Poi, altera il costruttore (o aggiungere un alloggio) della classe che si desidera segnalare i progressi per accettare un IProgressReporter. Questa è una forma di iniezione delle dipendenze e dovrebbe consentire in modo significativo all'oggetto di segnalare i progressi evitando allo stesso tempo dipendenze specifiche sulle librerie di threading.

+0

Si prega di controllare la mia modifica. – hattenn

+0

Ho aggiunto un esempio di codice che è possibile utilizzare. – Rob

1

Qui si può fare 2 modi diversi posterò entrambe le opzioni qui sotto per aiutare a punto nella giusta direzione

Si dovrebbe di farlo da un altro thread, e quindi l'aggiornamento del thread dell'interfaccia utente da questo thread. Stai bloccando ulteriori elaborazioni eseguendo questo lavoro sul thread dell'interfaccia utente.

Se può non spostare questo codice al thread UI, allora si potrebbe sempre chiamare Application.DoEvents, ma io fortemente suggerire di esplorare queste opzioni prima:

seconda alternativa si potrebbe fare qualcosa di simile:

Avrete bisogno di ottenere i dati da un thread all'altro. Questo può essere fatto in un paio di modi ...

In primo luogo, il thread "sfondo" potrebbe aggiornare una specie di variabile di stringa "CurrentStatus" che cambia mentre procede. È quindi possibile inserire un timer nel modulo in modo da acquisire la variabile CurrentStatus e aggiornare l'etichetta con esso.

In secondo luogo, è possibile semplicemente richiamare l'operazione dal thread in background al thread dell'interfaccia utente con un delegato che utilizza la proprietà InvokeRequired del controllo etichetta. Così, per esempio ...

private delegate void UpdateStatusDelegate(string status); 
private void UpdateStatus(string status) 
{ 
    if (this.label1.InvokeRequired) 
    { 
     this.Invoke(new UpdateStatusDelegate(this.UpdateStatus), new object[] { status }); 
     return; 
    } 

    this.label1.Text = status; 
} 

È possibile chiamare che UpdateStatus() il metodo da qualsiasi thread (UI o di fondo) ed è in grado di rilevare se o non ha bisogno di richiamare l'operazione sul thread dell'interfaccia utente principale (e se così, lo fa).

Modifica: Per impostare in realtà il filo, si può farlo in questo modo:

private void StartProcessing() 
    { 
     System.Threading.Thread procThread = new System.Threading.Thread(this.Process); 

     procThread.Start(); 
    } 

    private void Process() // this is the actual method of the thread 
    { 
     foreach (System.IO.FileInfo f in dir.GetFiles("*.txt")) 
     { 
      // Do processing 
      // Show progress bar 
      // Update Label on Form, "f.Name is done processing, now processing..." 
      UpdateStatus("Processing " + f.Name + "...");     
     } 
    } 

Poi, quando l'utente fa clic sul pulsante "GO" sarà sufficiente chiamare StartProcessing().

+0

Si prega di controllare la mia modifica. – hattenn