Afin de remplir une liste de manière asynchrone j’ai fais appel au BackgroundWorker.
Ce dernier me permet d’avoir un événement asynchrone bien pratique.
Pour le lancer j’utilise ce bout de code:
private void RefreshList()
{
while (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
Thread.Sleep(0);
}
backgroundWorker.RunWorkerAsync(txtFilter.Text);
}
Ce qui me permet d’appeler RefreshList aussi souvent que je veux même si l’ordre de refresh précédent n’est pas terminé.
Pour gérer le CancelAsync j’ai mis en place ce bout de code (dans le handler de l’événement BackgroundWorker.DoWork) :
foreach (var cardResult in list)
{
ListViewItem item = new ListViewItem(cardResult.Name);
if (backgroundWorker.CancellationPending)
break;
lstList.Invoke(new Action(
delegate()
{
lstList.Items.Add(item);
}
));
}
Ainsi de manière asynchrone ma liste se remplit depuis une requête LinQ.
Tout aurait du marcher et pourtant ca n’a pas marché :).
En effet, comme je suis un garçon poli je ne touche pas aux données de mes contrôles depuis un autre thread d’ou mon appel à lstList.Invoke qui me permet d’effectuer mon code lié au contrôle sur le thread du-dit contrôle.
Or, lorsque je fais ma boucle d’attente avec mon Thread.Sleep(0) (qui permet de laisser les autres threads prendre la main) je bloque le thread de mon contrôle, ce qui fait que mon Invoke ne peut jamais se faire!
Donc, pour bien utiliser le BackgroundWorker et surtout pour bien gérer le Cancel, il ne faut pas utiliser Thread.Sleep mais bien Application.DoEvents qui va permettre à mon thread principal de voir arriver l’ordre d’Invoke et donc qui finalement libérera le thread du BackgroundWorker qui pourra de ce fait voir que CancellationPending est à vrai.
La boucle de gestion du cancel donne donc:
private void RefreshList()
{
while (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
Application.DoEvents();
}
backgroundWorker.RunWorkerAsync(txtFilter.Text);
}
Ouf :)
.Net, Windows Forms