Nel mio (ormai famoso, dato che tutti mi chiedono notizie)
software di fatturazione, ho aggiunto ieri sera una bella StatusBar, nel bordo
inferiore della mia Windows Form con IsMdiContainer = true. Questa StatusBar non fa nulla di particolare, solo
visualizzare l'ora corrente in basso a destra. Solo che l'ora va continuamente
aggiornata perchè il tempo, non è una novità, passa e non si ferma mai.
Stamattina quindi ho creato una piccola classe
CurrentTimeUpdater che in modo asincrono, e sfruttando un Timer
interno, mi permette di aggiornare continuamente l'ora sul WF. Il vantaggio di
questa soluzione è che il tutto gira in un thread secondario, senza appesantire
la UI, mantenendo allo stesso tempo una certa semplicità. La classe
CurrentTimeUpdater ha un solo costruttore, a cui vanno
passati:
- un Control, che è quello su cui andrà scritta
l'ora
- un double, che è l'intervallo in millisecondi che
verrà impostato sul Timer interno
- un delegate, che punta alla funzione che aggiorna
realmente il controllo
Girando in un thread secondario, ad ogni tick del Timer interno lancio il
metodo BeginInvoke del controllo, così da evitare ogni sorta di
problemi in questo senso. La classe inoltre implementa l'interfaccia
IDisposable per rilasciare le risorse, primo fra tutti proprio
il Timer. Sulla mia WF ho gestito l'evento Load così:
private void Form1_Load(object sender, EventArgs e)
{
CurrentTimeUpdater wtc = new CurrentTimeUpdater(this, 1000,
new WriteCurrentTimeDelegate(WriteCurrentTime));
}
Il delegate WriteCurrentTimeDelegate punta ad una funzione implementata nella
WF in questo modo:
private void WriteCurrentTime(DateTime CurrentTime)
{
this.lblOra.Text = CurrentTime.ToLongTimeString();
}
Senza grosse complicazioni, quindi, la mia classe
CurrentTimeUpdater aggiorna la mia Label, gestendo
correttamente l'accesso alla UI da parte del thread. Ho preferito aggiornare
fisicamente il controllo sulla UI, perchè farlo nella mia classe avrebbe
significato utilizzare sempre e solo la proprietà Text della
classe Control. Dal momento che i controlli più complessi
possono avere diverse possibilità, ho preferito utilizzare un delegate, che non
fa altro che eseguire la function implementata. E' quindi compito della UI
sapere come deve essere aggiornata. Vi riporto per brevità il codice
dell'handler dell'evento Elapsed del Timer:
void TimerTick(object sender, System.Timers.ElapsedEventArgs e)
{
// Chiamo la BeginInvoke sul controllo
controlToWrite.BeginInvoke(updateFunction, e.SignalTime);
}
controlToWrite è il controllo. updateFunction è il delegate. SignalTime è una proprietà della classe
ElapsedEventArgs, derivata da EventArgs, del FX. Nulla di
particolarmente complicato.
Se a qualcuno dovesse servire, la classe CurrentTimeUpdater
è disponibile a questo indirizzo.