Sto sviluppando una applicazione abbastanza semplice usando
le Windows Forms del Framework .NET 1.1, talmente semplice che in due giorni è
bell'e pronta... quello che mi serve praticamente è una specie di procedura
wizard-like, avviabile da una applicazione unmanaged, per esportare una
serie di dati propri della applicazione stessa e generare un documento in
formato XML che descriva i dati esportati.
Essenzialmente quello che l'applicazione
deve fare è richiedere all'utente una serie di opzioni di configurazione,
chiedergli la cartella dove salvare i dati da esportare, e quindi avviare una
procedura di copia dei files e di generazione di un xml "descrittivo" dei dati
copiati... niente di sconvolgente, anzi! :)
Ovviamente la fase "di lavoro", cioè la copia
dei dati nella cartella di destinazione e la generazione del documento xml, deve
andare su un thread separato, e l'interfaccia deve rimanere "attiva" per
mostrare lo stato dei lavori (tramite ProgressBar) e attendere l'eventuale richiesta di
cancellazione delle operazioni...
Tutto questo richiede una "decente" progettazione della gestione
dei thread (cosa che tra l'altro credo Raffaele Rialdi
abbia affrontato ai Community Days...), e l'uso di Invoke e salse varie per comunicare con la UI... ma se siete pigri
come me e volete qualcosa di già pronto e utilizzabile per "lavori semplici" da
eseguire in background, ma non volete/potete usare il FW 2.0 e il suo controllo BackGroundWorker, potete usare il componente BackGroundWorker 1.1 e 2.0 compliant sviluppato da Francesco Balena!
Il codice del componente è disponibile qui, e devo dire che è molto versatile, anche rispetto
all'analogo componente fornito nel framework 2.0; ad esempio permette di
impostare la priorità del thread secondario, di assegnare un nome al thread
stesso, di terminare o sospendere il thread in esecuzione, di specificare se il
thread di esecuzione deve essere un oggetto System.Threading.Thread oppure un thread dal ThreadPool...
L'uso del componente è davvero triviale: si trascina nella form,
si impostano le proprietà e si implementani gli eventi DoWork (il metodo che verrà eseguito nel thread
secondario), ProgressChanged (in cui implementare
le modifiche della UI in risposta all'avanzamento dei lavori) e RunWorkerCompleted (quello che deve succedere quando il
lavoro è terminato).
Il metodo DoWork è
effettivamente quello basilare, va implementato in questo
modo:
private void backgroundWorker_DoWork(object sender, CodeArchitects.ComponentModel.DoWorkEventArgs e)
{
// Recupero degli argomenti da utilizzare all'interno del metodo
object[] args = (object[])e.Argument;
// Refresh della UI
backgroundWorker.ReportProgress(10, "Avvio lavori");
Thread.Sleep(1000);
// Richiesta terminazione anticipata del thread
if(backgroundWorker.CancellationPending) return
backgroundWorker.ReportProgress(50, "Prosecuzione lavori");
Thread.Sleep(1000);
if(backgroundWorker.CancellationPending) return
backgroundWorker.ReportProgress(90, "Terminazione lavori");
Thread.Sleep(1000);
e.Result = "Progetto PocketShArc esportato correttamente!";
}
All'interno del metodo DoWork occorre controllare periodicamente
che l'utente non abbia richiesto di interrompere i lavori: il componente ci
offre la proprietà (in sola lettura) backgroundWorker.CancellationPending, adatta allo
scopo.
Come già detto, il metodo ProgressChanged avvisa la UI che i
lavori stanno procedendo:
private void backgroundWorker_ProgressChanged(object sender, CodeArchitects.ComponentModel.ProgressChangedEventArgs e)
{
// Il valore da assegnare alla progressBar
progressBar.Value = e.ProgressPercentage;
// Testo da assegnare ad una label...
// o qualsiasi altra cosa visto che e.UserState è di tipo
object!
progressLabel.Text = e.UserState.ToString();
}
Al termine dei lavori invece...
private void backgroundWorker_RunWorkerCompleted(object sender, CodeArchitects.ComponentModel.RunWorkerCompletedEventArgs e)
{
progressBar.Value = 100;
// e.Result è anch'essa di tipo object, ed è stata impostata in DoWork
progressLabel.Text = e.Result.ToString();
}
Ok, tutto bello e facile, ma come cavolo si fa a farlo partire e
a interromperlo?
/*
* Avvio del componente passandogli alcuni argomenti
*
che recupererò poi nel metodo DoWork (in cui
infatti facevo il cast a object[] ...)
*/
object args = new object[] {"ciao", "mondo", "io lavoro in background"}
backgroundWorker.RunWorkerAsync(args);
/*
* Interruzione del componente: la cancellazione è ASINCRONA,
* in pratica viene impostato a true il valore backgroundWorker.CancellationPending:
* sarà il nostro codice, in DoWork, a dover gestire come comportarsi...
*/
backgroundWorker.CancelAsync();
In realtà è possibile pure accedere direttamente al Thread
secondario, e fare cose tipo BackgroundWorker1.Thread.Abort()
... ma a me sembra un pò una porcata...
Insomma, il componente merita più di uno sguardo, quindi vi
invito a darci una occhiata
L'unica cosa... nel codice del componente e nella pagina di
download dello stesso non si parla di licenze d'uso, o almeno io non ho visto
nessun accenno a restrizioni di alcun tipo... spero questo significhi che il
codice è rilasciato "as is" ed è utilizzabile senza limitazioni...
powered by IMHO 1.3