[Résolu][C#] Threads & UI

Salut la zone,

alors voilà, j’ai un petit problème qui ressemble à celui de metalDestroyer mais j’utilise pas mes WPF (je savais même pas ce que c’était). A la base c’est pas le genre de dev’ que je fais donc j’ai un peu de mal.

Alors voilà, j’ai crée un Winform avec un bouton qui permet de sélectionner un dossier, une barre de progression, un TexBox pour afficher ce que fait le processus, un bouton start et un bouton stop.

L’idée est que lorsque l’utilisateur clique sur le bouton start, ça va chercher les fichiers du dossier et ça fait des traitements.
Bon du coup l’UI freeze et j’ai pas accès à mon bouton arret. J’ai essayé de faire un thread basique mais lorsque j’essaie de rafraichir mon textbox, il m’envoie ch****.

J’ai pas réussi a trouver un exemple clair et simple, est-ce que quelqu’un pourrait m’aider svp ?

J’étais parti sur un truc du genre :

[code] public bool stopRequest = false;

	private void btStart_Click(object sender, EventArgs e)
	{
		stopRequest = false;
		listing();
	}


   private void listing()
   {
			foreach (FileInfo fi in finfo)
			{
				if (stopRequest == false)
				{
					 //Do many things
					 progressBar1.Increment(1);
					 textboxInfo.Text += "Document traité";
				} else {
					break;
				}
			}
   }

	private void btStop_Click(object sender, EventArgs e)
	{
		stopRequest = true;

	}[/code]

Merci d’avance :smiley:

Le probleme est probablement que quand tu rafraichis ta textbox, tu le fais sur le thread de traitement. WinForms (et WPF aussi d’ailleurs) ne veulent etre modifies que sur le thread d’UI. Pour faciliter (vaguement) la tache, tous les controles exposent 2 trucs: une propriete “InvokeRequired”, qui te dira si oui ou non t’es sur le thread UI, et des methodes “Invoke” et “BeginInvoke”, qui permettent de dire a un controle de faire un truc sur le thread UI (l’une bloque le thread courant, l’autre non).

Donc globalement, tant que t’es sur ton thread de traitement, plutot que de changer ta textBox directement, tu va vouloir appeller Invoke ou, mieux, BeginInvoke, et passer une delegate qui fait le changement. Pour faire un truc plus general, tu peux tester la valeur de InvokeRequired sur la textBox. Si ca retourne vrai, hop, t’appelles BeginInvoke. Si ca retourne faux, tu peux faire le changement directement.

[quote=“lordabdul, post:2, topic: 50390”]Le probleme est probablement que quand tu rafraichis ta textbox, tu le fais sur le thread de traitement. WinForms (et WPF aussi d’ailleurs) ne veulent etre modifies que sur le thread d’UI. Pour faciliter (vaguement) la tache, tous les controles exposent 2 trucs: une propriete “InvokeRequired”, qui te dira si oui ou non t’es sur le thread UI, et des methodes “Invoke” et “BeginInvoke”, qui permettent de dire a un controle de faire un truc sur le thread UI (l’une bloque le thread courant, l’autre non).

Donc globalement, tant que t’es sur ton thread de traitement, plutot que de changer ta textBox directement, tu va vouloir appeller Invoke ou, mieux, BeginInvoke, et passer une delegate qui fait le changement. Pour faire un truc plus general, tu peux tester la valeur de InvokeRequired sur la textBox. Si ca retourne vrai, hop, t’appelles BeginInvoke. Si ca retourne faux, tu peux faire le changement directement.[/quote]

Ok merci, j’ai essayé, déjà il m’envoie plus chier mais par contre quand j’essaie de récupérer la valeur présente dans la textbox contenant l’url du folder, c’est pareil il aime pas. Il faut aussi faire pareil je suppose sauf que la methode invoqué renvoie mon string ?

Tu peux utiliser un BackgroudWorker pour ce genre de traitements, über pratique :smiley: (System.ComponentModel.BackgroundWorker).

Pas mieux que Loardabdul. Un peu de code pour expliquer ça :

[code]public bool stopRequest = false;

private void btStart_Click(object sender, EventArgs e)
{
stopRequest = false;
// Lancement de la tache en asynchrone
System.Threading.ThreadPool.QueueUserWorkItem(this.listing);
}

private void listing(object state)
{
foreach (FileInfo fi in finfo)
{
if (stopRequest)
return;

	// Ici tu copies

	// Appel de la méthode qui va actualiser l'UI, on le fait en synchrone pour être sur que l'UI se mette à jour à chaque copie
	// Utiliser BeginInvoke n'aurait pas changé grand chose
	Invoke((MethodInvoker)this.ReportProgress);
}

// Inutile de bloquer ce thread pour termine le traitement, on appelle la méthode de fin du traitement dans l'UI
// et on quitte la tâche
BeginInvoke((MethodInvoker)this.WorkCompleted);

}

private void ReportProgress()
{
progressBar1.Increment();
}

private void WorkCompleted()
{
// Code à effectuer une fois le traitement terminé
}

private void btStop_Click(object sender, EventArgs e)
{
stopRequest = true;
}[/code]

Ca reste la méthode “roots” pour apprendre.

Tu peux aussi utiliser BackgroundWorker comme l’a dit Galad qui fait toute cette tambouille à ta place.

en gros, ca va ressembler a ca :

[code]// dans ta classe :
private delegate void AList();

// dans ta fonction ou ton handling d’event.
if (m_ListView.InvokeRequired)
{
m_ultraListView.Invoke((AList) delegate
{
AddNewEntry(e.Entry);
});
}
else
{
AddNewEntry(e.Entry);
}[/code]

Et jettes un oeil du coté du BackgroundWorker pour ce genre de truc.

La règle c’est : aucun appel sur aucun contrôle depuis un thread autre que le thread graphique.

Donc la solution c’est de stocker la valeur dans une variable privée de ta classe avant de lancer le traitement asynchrone. Puis ton thread ira lire la valeur de cette variable.

Item pour mettre à jour la ProgressBar. Comme dans mon exemple, on ne peut pas interagir avec les contrôles depuis un thread différent de l’UI Thread. Donc tu dois utiliser une méthode anonyme (exemple d’Ana-L) ou une méthode appellée via (Begin)Invoke.

Merci bien pour toutes vos réponse, je vois qu’il n’y a pas une seule manière de résoudre ça :smiley: .

Bon je vais essayer les différentes méthode, mais demain, parce que là j’ai le cerveau qui fond. :smiley:

Merci à tous, j’ai résolu ça avec des BeginInvoke ! :slight_smile: