BackgroundWorker e le windows forms

di Andrea Zani, in Memo,

Apprezzo moltissimo questo oggetto introdotto nella versione 2.0 del framework quando devo scrivere delle windows forms tradizionali con il Framework. Chi ha esperienza di programmazione delle windows application con .net, saprà del famoso freezing che colpisce l'applicazione in caso di operazioni lunghe/pesanti se tale codice viene elaborato da un evento (click su un button o simile).

Fin dalla versione 1.0 per evitare questo inconveniente era necessario armarsi di delegate e pazienza, e con il classico inconveniente di non poter aggiorare l'interfaccia grafica in caso di utilizzo di questa tecnica se non con l'uso di altri artefizi. Ma veniamo al dunque. Creata una windows forms con un button e una label, voglio fare in modo che alla pressione del button venga visualizzato il messaggio di attesa, e solo alla fine dell'operazione vedere il risultato.

Con il BackgrounWorker la cosa è semplice, anzi, possiamo anche aggiornare l'interfaccia grafica durante l'elaborazione e con poco altro codice permettere il blocco dell'operazione.

backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;

Nella prima riga specifico quale evento sarà chiamato quando dovrà essere avviata l'operazione pesante. La seconda sarà chiamata alla fine della pesante operazione. La terza, che possiamo invocare noi dal primo evento, per aggiornare eventualmente l'interfaccia grafica. Sì, perché solo questo evento e il secondo potranno aggiornare l'interfaccia grafica.

backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;

Queste righe, invece, attivano la possibilità di avviare il terzo evento e di poter bloccare le operazioni da interfaccia utente. Obbligatori, sono solo l'evento DoWork e RunWorkerCompleted.

Ma è ora di cominciare. Ecco l'evento del Button nella pagina:

private void button1_Click(object sender, EventArgs e)
{

    // Disabilito il button di avvio
    button1.Enabled=false;
    // Abilito il button di cancel
    button2.Enabled=true;
    backgroundWorker1.RunWorkerAsync(100);
}

Possiamo passare un parametro facoltativo al nostro codice, di qualsiasi tipo. Però innanzitutto disabilitiamo il button in modo che non si possa cliccare più volte sopra di esso. Con il method RunWorkerAsync, avviamo il codice presente nell'evento DoWork.

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    // Leggo il parametro
    int val = int.Parse(e.Argument.ToString());

    // Richiedo il Backgroundworker che ha richiamato l'evento
    // per poter richiamare l'evento per l'aggiornamento
    BackgroundWorker worker = sender as BackgroundWorker;

    for (int i = 0; i < val; i += 1)
    {
        // Richiama l'evento memorizzato in ProgressChanged
        // Questo evento accetta come parametro un valore
        // compreso da 0 a 100 (%)
        worker.ReportProgress(i);
        System.Threading.Thread.Sleep(40);

        // Controllo se è stato premuto il button di Cancel
        if (worker.CancellationPending)
        {
            e.Cancel = true;
            break;
        }
    }
    // Infine passo l'eventuale risultato all'evento finale
    e.Result = DateTime.Now;
}

Per aggiornare la window durante l'elaborazione viene utilizzato il method ReportProgress che accetta come parametro un intero compreso tra zero e cento (percentuale di elaborazione). Ecco il codice usato dall'esempio:

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Visualizzo aggionamento percentuale nella label
    label1.Text = string.Format("{0}%", e.ProgressPercentage);
}

Interessante, di questo evento, che possiamo aggiornare qualsiasi oggetto nella window, anche datagridview con il simpatico effeto grafico di popolamento dello stesso. Non potendolo passare come parametro è sufficiente definire la fonte dati dello stesso come property nella classe della pagina,

Ed ecco il codice eseguito automaticamente alla fine di questo codice:

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Controllo se l'operazione è stata bloccata dall'utente
    if (e.Cancelled)
    {
        label1.Text = "Operation Cancelled";
    }
    else
    {
        // Visualizzo la data passata dalla elaborazione
        DateTime dt = DateTime.Parse(e.Result.ToString());
        label1.Text = dt.ToString();
    }

    // Riattivo il button di avvio
    button1.Enabled = true;

    // E disabilito il button di cancel
    button2.Enabled = false;
}

L'operazione di Cancel è collegato con un normale evento di button2:

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

Banalmente, segnala al nostro oggetto che è stata richiesta la fine dell'elaborazione del nostro codice. Questo lo possiamo controllare, come si vede sopra, controllando l'evento CancellationPending. Ovviamente questo operazione è controllabile solo se è in elaborazione il nostro codice. Eventuale richieste pesanti al DB non potranno essere bloccate fino alla restituzione dei dati (senza scomodare ancora i delegate asincroni).

Me lo sono segnato.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese