Partage
  • Partager sur Facebook
  • Partager sur Twitter

Fonctions asynchrones et winforms

Modifier un objet winforms

Anonyme
    22 juin 2011 à 12:12:53

    Bonjour à tous :)

    Je sais qu'il est impossible de modifier un objet winforms depuis un autre thread et qu'il faut passer par des delegates. Mais je me demandais si il y avait pas une solution moins lourde que de créer un delegate pour chaque objet que l'on veut modifier ? Un delegate générique par exemple.

    Merci de votre aide,
    Liek.
    • Partager sur Facebook
    • Partager sur Twitter
    Anonyme
      22 juin 2011 à 16:17:25

      Dans le namespace System, il y a Action et Func.
      Action représente un delegate avec de 0 à 16 paramètres (il y a Action, Action<T>, Action<T1, T2>, jusqu'à celui avec 16 T) et qui ne retourne rien.
      Func représente un delegate avec de 0 à 16 paramètres et qui retourne une valeur (il y a Func<TResult>, Func<T, TResult>, jusqu'à 16 T et TResult).

      Exemples :

      Action
      

      Un delegate qui ne prend aucun paramètre et ne retourne rien.

      Action<int, string>
      

      Un delegate qui prend un int et un string en paramètres, sans rien retourner.

      Func<int>
      

      Un delegate qui ne prend aucun paramètre et qui retourne un int.

      Func<string, int>
      

      Un delegate qui prend un string en paramètre et qui retourne un int.

      Func<int, int, int, int, char, string, string, string, string, bool, bool, bool, bool, bool, bool, bool, int>
      

      Fans de programmation fonctionnelle, bonsoir... :-° (un delegate qui prend quatre int, un char, quatre string et sept bools en paramètres et qui retourne un int)
      • Partager sur Facebook
      • Partager sur Twitter
      Anonyme
        22 juin 2011 à 16:35:30

        Merci de ta réponse,

        Mais tu pourrais me donner un exemple sur le comment modifier par exemple le texte d'un label dans un autre thread avec les classes que tu ma montrés ?
        • Partager sur Facebook
        • Partager sur Twitter
          22 juin 2011 à 17:04:00

          Bonjour,

          @Aethec, question programmation fonctionnelle j'aurais tendance (et ça m'arrive souvent en plus) à écrire ton dernier exemple comme ceci
          Peut piquer très fort les yeux !


          Func<int, Func<int, Func<int, Func<int, Func<char, Func<string, Func<string, Func<string, Func<string, Func<bool, Func<bool, Func<bool, Func<bool, Func<bool, Func<bool, Func<bool, int>>>>>>>>>>>>>>>>
          

          Pour pouvoir curryfier à loisir, je me permet par contre d'éviter la dénomination textuelle sur ce coup ça me prendrait 4 lignes o_O
          Et encore je reste sympa, j'évite d'écrire la version VB.Net, même si c'est le plus souvent dans ce langage que je les écris.


          @Liek voici un exemple succin de récupération puis changement du texte d'un label dans le cadre d'un accès depuis un thread autre que le thread principal :
          string oldText = label.Invoke(new Func<string>(() => label.Text));
          label.Invoke(new Action(() => label.Text = newText));
          

          Plusieurs remarques :
          • La méthode Invoke appartient à la classe Control et est par conséquent utilisable depuis n'importe lequel (souvent si on se trouve dans le Form on utilise juste Invoke, alias pour this.Invoke ce qui au final est la même chose*)
          • Je n'ai pas VS sous la main pour tester le code (donc je suis pas à l'abri d'une erreur) mais je ne suis pas certain qu'il soit obligé d'instancier explicitement le Func ou le Action ; autrement dit ceci me "semble" équivalent (et plus court) mais à vérifier :
          string oldText = label.Invoke(() => label.Text);
          label.Invoke(() => label.Text = newText);
          
          Enfin, il existe également des méthodes BeginInvoke (et EndInvoke), pour réaliser ceci également de manière asynchrone.
          *En fait ce n'est pas tout à fait la même chose, comme Invoke cherche dans la hiérarchie de contrôle le premier qui possède un handle sur le contrôle affecté, comme le Form en a toujours (il me semble) un, passer directement par lui, raccourci (de très peu) cette recherche.
          </span>

          Cordialement !
          • Partager sur Facebook
          • Partager sur Twitter
          Censément, quelqu'un de sensé est censé s'exprimer sensément.
          Anonyme
            22 juin 2011 à 17:18:28

            @Sehnsucht:
            Ca fait un peu genre "Inception" avec des génériques... (me dis pas que tu écris souvent des Func à 16 paramètres :-° )

            Ton deuxième exemple marche pas, par contre. 2x "Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type".
            • Partager sur Facebook
            • Partager sur Twitter
            Anonyme
              22 juin 2011 à 17:44:21

              Merci pour vos réponses, sa m'a déjà aider mais j'ai oublié de préciser que je codé en VB.NET et bien que je comprend relativement le c# j'ai eu un peu de mal en reproduisant le code :


              FrmWait.Invoke(New Action(Function(text As String) (FrmWait.Text = text)("")))

              Et il me dit que la signature imbriquée n'est pas compatible avec le delegate ?
              • Partager sur Facebook
              • Partager sur Twitter
                22 juin 2011 à 18:47:25

                Bon pour le second, j'étais pas sûr de moi je l'avais précisé !

                @Aethec:
                des Func à 16 paramètres non jamais (déjà je curryfie systématiquement, donc j'ai que des Action à 1 (seulement In) paramètre maxi et des Func à 2 paramètres maxi (seulement Out ou In/Out) ^^
                mais récemment j'ai eu des trucs dans ce style:

                using myDict = System.Collections.Generic.Dictionary<char, System.Func<double, System.Func<double, double>>>;
                using myFunc = System.Func<double, System.Func<double, double>>;
                private static Func<myDict, Func<Func<char>, Func<Action, myFunc>>> Operation //...
                

                ou ceci (mais là c'était plus pour m'entrainer à manipuler tout ça ;) )
                public static Func<Func<T, Func<R, Func<R, R>>>, Func<R, Func<Tree<T>, R>>> FoldTree<T, R>() //...
                public static IEnumerable<T> InOrder<T>(Tree<T> t)
                {
                    return Tree<T>.FoldTree<T, Func<IEnumerable<T>, IEnumerable<T>>>()(data => left => right => acc => left(new T[] { data }.Concat(right(acc))))(acc => acc)(t)(new T[] { });
                }
                



                @Liek:
                Pas certain de mon coup (j'ai toujours pas VS sous la main) mais au vu de ce que tu tentes de faire tu devrais essayer ceci:
                FrmWait.Invoke(New Action(Sub() FrmWait.Text = "")) 'si c'est bien un texte vide que tu veux
                
                Dim newText as String = "un texte récupéré ailleurs"
                FrmWait.Invoke(New Action(Sub() FrmWait.Text = newText)) 'pour un texte stocké ailleurs (accessible dans la portée de l'appel à Invoke)
                'ou
                FrmWait.Invoke(New Action(Sub(newText) FrmWait.Text = newText), { "le nouveau texte" }) 'perso j'utilise peu cette version, donc même pas sûr qu'elle s'écrive comme ça
                

                Une fois de plus, plusieurs remarques :
                • FrmWait semble être (au nom) un Form, à vérifier mais il me semble que pour changer son Text pas besoin de Invoke, tu peux le faire directement.
                • Corrolaire du point précédent, tu as peut-être fait une erreur en postant, et tu voulais utiliser un truc style FrmWait.Label1.Text (ce qui justifierait l'usage du Invoke et collerait avec ta demande évoquant un label)
                • Enfin, j'avoue ne jamais avoir essayer mais tu sembles utiliser ce code depuis un autre Form (sinon ça serait Me.Invoke), techniquement en POO c'est pas tip-top, à cause du principe de responsabilité unique, ça devrait être au Form qui possède le label de posséder le code qui modifie celui-ci)


                Cordialement !
                • Partager sur Facebook
                • Partager sur Twitter
                Censément, quelqu'un de sensé est censé s'exprimer sensément.
                Anonyme
                  23 juin 2011 à 18:47:26

                  Merci beaucoup , j'ai compris l'utilisation et sa marche très bien..., ou pas. Voila, je fait un programme en réseaux, donc, avec un client, et un serveur. Le serveur est programmé avec des sockets asynchrones. Pour le client, je voudrais utiliser des sockets synchrones pour pouvoir inter-agir avec les objets winforms facilement. Donc un seul thread, mais le problème c'est que je vois pas comment pouvoir réceptionner les packets sans faire une boucle infinie qui va ensuite bloquer l'éxécution des méthodes qui suivent. Je m'explique :

                  Public sub Main()
                  
                     Receive() ' Hop on lance la reception des packets
                  
                     ' Ces méthodes ne seront pas exécutes à cause de la boucle infinie de réception plus haut
                  
                     MethodQuiFaitUnTruc() 
                     MethodQuiEcrisQueqChose()
                  
                  End sub
                  
                  Public sub Receive()
                  
                    ' La fameuse boucle infinie qui bloque
                  
                    While true
                  
                    ' code de tete d'illustration
                  
                     if Socket.DataAvailable then
                  
                        Socket.Receive(Buffer)
                        Packet = Encoding.UTF8.Getstring(Buffer) ' etc..
                  
                        Application.DoEvents() 
                        Thread.Sleep(1)
                  
                     end if
                  
                    End while
                  
                  End sub
                  


                  Et si je fait un autre thread avec sa :

                  Dim NewThread as new Thread(AddressOf Receive) NewThread.Start()

                  En faisant sa : FrmMain.Invoke(new Action(Sub() ObjetWinForms.Text = "montexte")) sa me fait cette erreur :

                  Invoke or BeginInvoke cannot be called on a control until the window handle has been created.


                  Merci d'avance.
                  • Partager sur Facebook
                  • Partager sur Twitter

                  Fonctions asynchrones et winforms

                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                  • Editeur
                  • Markdown