Timer qui pose problème. [Résolu]

vikingch 8 Messages postés jeudi 30 janvier 2014Date d'inscription 20 mars 2014 Dernière intervention - 5 févr. 2014 à 14:49 - Dernière réponse : nagaD.scar 4269 Messages postés samedi 8 septembre 2007Date d'inscription 29 août 2018 Dernière intervention
- 6 févr. 2014 à 17:19
Bonjour à tous :)

Mon application en C#/WPF se compose de tuiles à la façon Windows 8 et d'un menu avec du texte qui défile.

Le rafraichissement des données affichées dans ces tuiles se font à l'aide d'un timer.

Le problème c'est qu'a chaque "boucle" effectué par le timer, le texte défilant a une saccade, comme si l'application se figeait le temps que la procédure appelée par le timer ait fini de s'exécuter.

J'ai essayé timer, dispachertimer backgroundworker, rien à faire cette maudite saccade persiste.

Toute aide sera plus que bienvenue (je ne suis pas un pro en c#) !
Afficher la suite 

Votre réponse

9 réponses

nagaD.scar 4269 Messages postés samedi 8 septembre 2007Date d'inscription 29 août 2018 Dernière intervention - 5 févr. 2014 à 15:25
0
Merci
salut,

Je ne vois pas ton code, mais de ce que je peux dire c'est simplement que ton rafraichissement n'est pas implémémenté dans un thread.

Ton timer va déclencher au bout de n millisecondes une fonction qui va implémenter ta liste (je suppose que c'est une liste[ou plutot 1 par tuile, peu importe]).

En fait, il faut non pas que tu appel directement la fonction, mais que tu la lance via un thread afin que ton composant graphique ne subisse pas de "freeze". Fais attention lorsque tu souhaite mettre à jours tes éléments graphiques depuis un thread : tu ne peux pas agir directement depuis ton thread, il faut utiliser "Invoke".

si tu veux un coup de main sur le code, il me faut le contenu de la fonction d'évènement de ton timer ainsi l'ajout de tes éléments à tes listes (tuiles).

naga
Commenter la réponse de nagaD.scar
vikingch 8 Messages postés jeudi 30 janvier 2014Date d'inscription 20 mars 2014 Dernière intervention - 5 févr. 2014 à 15:59
0
Merci
Salut, merci pour ta réponse.

En fait c'est un label par tuile (tuile faites avec des wrappanel) qui affiche un chiffre.

Le timer est déclaré dans le MainWindow() :

public MainWindow(){

InitializeComponent();
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 10);
dispatcherTimer.Start();
}
La procédure suivante est lancée toutes les 10 secondes :


private void dispatcherTimer_Tick(object sender, EventArgs e)
{
NbServers.Content = checkAlerte("System Down");
WpServer.Background = colorCheck(Convert.ToInt16(NbServers.Content));

NbBackup.Content = checkAlerte("Backup");
WpBackup.Background = colorCheck(Convert.ToInt16(NbBackup.Content));

NbAlerts.Content = checkAlerte("Alerts");
WpAlerts.Background = colorCheck(Convert.ToInt16(NbAlerts.Content));

etc... etc.. (une douzaine de tuiles)

Et la fonction checkAlerte() se présente comme ceci :


private int checkAlerte(string alerteType){
ConnexionDB Mycon = new ConnexionDB();
int value = 0;
try{
Mycon.req = "Select count(computerName) from custom_TV_Alerts where description = '" + alerteType + "'";
Mycon.ExecuterDB();
while (Mycon.rdr.Read()){
value = Mycon.rdr.GetInt32(0);
}
Mycon.FermerDB();
}
catch{
}
return value;
}

J'ai fait des recherches, même en anglais, mais je suis incapable de trouver une solution à mon problème, donc merci beaucoup pour ton aide ;)
nagaD.scar 4269 Messages postés samedi 8 septembre 2007Date d'inscription 29 août 2018 Dernière intervention - 5 févr. 2014 à 16:26
ok je vois le principe, donc dans ton cas ce qu'on va faire c'est dans un premier temps une récupération des résultats dans un thread, et dans un second temps la mise à jours de tes éléments graphique.


voila les méthodes :


  //--le delegate te permettra d'appeler une fonction via l'invoke
  public delegate void parListInt(List<int> li);
  
  //--cette fonction mettra à jour les champs graphiques à partir de la liste d'entier
  public void setBackground(List<int> li)
  {
   if (this.InvokeRequired)//--SI on est dans un autre thread que le graphique, on doit faire l'invoke pour interargir avec la form
   {
    this.Invoke(new parListInt(setBackground),li);//--voilà l'utilité de l'invoke
    return ;//--du coup on fait un return de manière à ce que la suite ne soit pas executé. on peu tout aussi bien encadrer la suite dans un else à la place
   }
   
   //-- si on atteint ce code, on peu éditer la form
   
   if( li.Count() == 5) //--ici ca sera ton nombre de tuile
   {
    WpServer.Background = li[0]; 
   .
   .
   .
   //--etc. pour toutes les tuiles
   }
   
   
  }
  
  
  //--ici notre thread qui va récupérer les valeurs
  public void th_getBackground(List<string> content)
  {
   List<int> li = new List<int>();
   foreach(string s in content)
   {
    li.Add(colorCheck(Convert.ToInt16(s));
   }
   setBackground(content);
  }
  
  //--il ne reste plus que la méthode appelante, qui est en fait dclenché par l évenement timer
  
  private void dispatcherTimer_Tick(object sender, EventArgs e)
  {
   //--on récupère les content pour notre thread
   List<string> lContent = new List<string>();
   
   NbServers.Content = checkAlerte("System Down");
   lContent.Add(NbServers.Content))
  
   etc... etc.. (une douzaine de tuiles)
   
   //--et on déclenche le thread
   Thread t = new Thread(() => th_getBackground(lContent));
   t.Start();
  }



dans mon idée, j'ai préféré passer par des list plutot qu'énumérer les paramètres (1 par tuile) de manière à ce que le thread n'ai pas à être modifié quelque soit le nombre d'éléments. Libre à toi d'éditer et changer comme tu le sens. De même j'ai mis comme test :
if( li.Count() == 5)
, il faudra bien sûr soit que tu indique le nombre de tuiles exacte, soit rien : tu vois ;)

naga
Commenter la réponse de vikingch
vikingch 8 Messages postés jeudi 30 janvier 2014Date d'inscription 20 mars 2014 Dernière intervention - Modifié par vikingch le 6/02/2014 à 09:59
0
Merci
Merci pour ton aide. J'ai plusieurs questions car :

1. Rien ne fonctionne, lol (je m'en doutais en peu car rare sont les exemples sur le net qui fonctionnent, à croire que j'ai une version génétiquement modifiée de c#)

Par exemple :

Error 22 The type or namespace name 'Thread' could not be found (are you missing a using directive or an assembly reference?)

J'ai un using System.Threading.Tasks; ,je sais pas si ça a quelque chose à voir...

Error 15 The best overloaded method match for 'System.Collections.Generic.List<int>.Add(int)'

Error 10 'monitoringSD.MainWindow' does not contain a definition for 'InvokeRequired' and no extension method 'InvokeRequired' accepting a first argument of type 'monitoringSD.MainWindow' could be found (are you missing a using directive or an assembly reference?)


Pas mal d'erreur de conversion de type, mais c'est pas le plus embêtant je pense.

2. J'ai passé un moment pour comprendre ton code et je pense avoir compris ce qu'il faisait mais le NbServers.Content = checkAlerte("System Down"); ne devrait-il pas ête dans le thread également ? Car dans ce cas-ci le thread gère seulement le changement de couleur de fond du wrappanel ?

Mon cerveau fait de la fumée ^^'
nagaD.scar 4269 Messages postés samedi 8 septembre 2007Date d'inscription 29 août 2018 Dernière intervention - 6 févr. 2014 à 10:25
pour les thread, le using est


using System.Threading;

invoke en wpf est avec Dispatcher (http://msdn.microsoft.com/fr-fr/library/system.windows.threading.dispatcher(v=vs.110).aspx), ecris donc plutot


this.Dispatcher.Invoke(new parListInt(setBackground),li);

(je n'ai pas testé l'invoke via le dispatcher mais de ce que j'ai pu vois la synthaxe est bonne).

(en fait s'il te manque des using, normalement un clic droit sur l'erreur-> résoudre te propose les using)

Pour le second point, tu as raison je me suis mélangé entre les deux, en fait ton thread te permettra de faire les traitement qui prennent du temps (communication avec ta base sql par exemple), et faire transiter les résultats.
En fait le découpage est simple :

- 1 méthode "évènement" (déclenché par quelque chose, ici le timer => elle déclenche notre thread)
- 1 méthode à implémenter dans un thread, qui ne va pas échanger avec l'interface graphique (ou un autre thread, mais on est plus souvent tenté de lire une saisie ou ajouter à une liste depuis le thread. bref). Elle a pour but de lancer les traitement dans un processus qui lui est propre (il ne sera pas bloquant pour le reste de ton appli).
- les mise à jours graphiques : les traitement sont souvent dans le but d'ajouter des info graphiquement (en tout cas dans ton cas). Le thread ne pouvant pas éditer directement la form, il faut trouver un moyen de lui donner les info à mettre à jours, en utilisant l'invoke donc.


Si tu as des erreurs que tu ne saurais pas résoudre, copie le code qui pose problème ici, sinon je peux pas vraiment t'aider.

naga
vikingch 8 Messages postés jeudi 30 janvier 2014Date d'inscription 20 mars 2014 Dernière intervention - 6 févr. 2014 à 11:01
Ok ça roule, le thread fonctionne ;) seulement mon problème de départ persiste ^^' la vérité doit donc être ailleurs... j'ai fait un teste avec le strict minimum :


public void setBackground()
{
this.Dispatcher.Invoke((Action)(() =>
{
NbServers.Content = checkAlerte("System Down");
NbBackup.Content = checkAlerte("Backup");
NbAlerts.Content = checkAlerte("Alerts");
NbLowDisk.Content = checkAlerte("Low Disk");
NbEvents.Content = checkAlerte("Events");
NbExchange.Content = checkAlerte("MSExchange");
NbDNS.Content = checkAlerte("DNS");
Nbinbox.Content = checkInbox();
NbVmx.Content = checkAlerte("Trilead VMX");
NbNetwork.Content = checkAlerte("Network");
NbPatch.Content = checkAlerte("Patch");
NbProcess.Content = checkAlerte("Process");
NbServices.Content = checkAlerte("Services");
}));
}


private void dispatcherTimer_Tick(object sender, EventArgs e)//(object sender, DoWorkEventArgs e)//object sender, EventArgs e)EventArgs ElapsedEventArgs
{
Thread t = new Thread(() => setBackground());
t.Start();
}

J'ai même mis la doubleanimation qui fait défiler le texte du bas, le fameux texte qui se fige 1-2 seconde lors de l'exécution du code dans le timer... mais ce maudit mini-freeze persiste.
nagaD.scar 4269 Messages postés samedi 8 septembre 2007Date d'inscription 29 août 2018 Dernière intervention - 6 févr. 2014 à 11:31
le problème de freeze vient justement de là :

            this.Dispatcher.Invoke((Action)(() =>
                {
                    NbServers.Content = checkAlerte("System Down");
                    NbBackup.Content = checkAlerte("Backup");
                    NbAlerts.Content = checkAlerte("Alerts");
                    NbLowDisk.Content = checkAlerte("Low Disk");
                    NbEvents.Content = checkAlerte("Events");
                    NbExchange.Content = checkAlerte("MSExchange");
                    NbDNS.Content = checkAlerte("DNS");
                    Nbinbox.Content = checkInbox();
                    NbVmx.Content = checkAlerte("Trilead VMX");
                    NbNetwork.Content = checkAlerte("Network");
                    NbPatch.Content = checkAlerte("Patch");
                    NbProcess.Content = checkAlerte("Process");
                    NbServices.Content = checkAlerte("Services");
                }));


du fait que tu invoke, tu indique que c'est à ton composant graphique de faire ces traitements, d'où l'idée de faire les test dans un thread et implémenter un tableau avec les résultat, et, durant l'invoke, seulement assigner les résultats. ce que tu peux faire c'est plutot :

 
public void setBackground()
{
string content;

content = checkAlerte("System Down");
this.Dispatcher.Invoke((Action)(() =>
{
NbServers.Content = content;
}));

content = checkAlerte("Backup");
this.Dispatcher.Invoke((Action)(() =>
{
NbBackup.Content = content;
}));

content = checkAlerte("Alerts");
this.Dispatcher.Invoke((Action)(() =>
{
NbAlerts.Content = content;
}));
//etc.


//
}



de cette manière c'est plus facilement lisible : tu récupère le résultat puis tu assigne. je trouve ca moins joli mais bon ^^
vikingch 8 Messages postés jeudi 30 janvier 2014Date d'inscription 20 mars 2014 Dernière intervention - 6 févr. 2014 à 16:39
Merci beaucoup pour ton aide, j'ai compris le principe et le tout fonctionne :)
nagaD.scar 4269 Messages postés samedi 8 septembre 2007Date d'inscription 29 août 2018 Dernière intervention - 6 févr. 2014 à 17:19
pas de soucis, si c'est bon passe le problème en résolu =)

n'hésite pas si besoin.
bon dev

naga
Commenter la réponse de vikingch

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.