IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Silverlight Discussion :

Multi-threading: Gestion de l'UI et de WebServices.


Sujet :

Silverlight

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut Multi-threading: Gestion de l'UI et de WebServices.
    Bonjour,

    Je suis actuellement stagiaire en développement et programmation d'applications. Je dois réaliser une application en Silverlight et C# communicant avec un Web-service. Voila pour la petite introduction.

    J'ai déjà conçu l'interface et le plan général du fonctionnement de mon programme et j'arrive a lier sans problèmes des évènements avec mon Web-service pour récupérer des données. Je réalise donc de simples actions qui en fonctions d'une touche ou d'un bouton vont extraire une information par le Web-service. Mais maintenant je me heurte a des problèmes et questions:

    En effet je veux que toutes les 2 secondes mon programme envoie une WebRequest (un genre de KeepAlive) qui mette a jour une liste de données(amis, messages, etc...), qui sera directement retransmise sur l'UI. Je souhaite aussi pouvoir conserver des évènements indépendants comme la pressions de boutons, le remplissage de "TextBox", qui influeront eux aussi sur l'Interface Utilisateur.

    Je sais que je vais devoir utiliser le multi-threading, mais Silverlight refuse que l'on puisse changer l'UI dans un Thread de fond, il impose qu'on le face a partir du Thread UI. Je sais que je dois utiliser le "Dispatcher" et le "Dispatcher.Timer" mais je ne sais pas comment les utiliser.

    Je sollicite donc votre aide pour m'aider a réaliser une sorte de plan avec les fonctions principales que devra comporter mon programme pour pouvoir fonctionner le plus facilement possible.

    Merci d'avance a ceux qui m'aideront,

    Ephismen.

    [EDIT] Une petite édition pour signaler que je suis en SL4, Visual Studio 2010 et .NET Framework 4, pas forcement important mais on ne sais jamais.

  2. #2
    Membre Expert
    Avatar de Samuel Blanchard
    Homme Profil pro
    Expert .NET
    Inscrit en
    Février 2010
    Messages
    1 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France

    Informations professionnelles :
    Activité : Expert .NET

    Informations forums :
    Inscription : Février 2010
    Messages : 1 504
    Par défaut
    Des infos sur le DispatcherTimer avec un exemple

    http://msdn.microsoft.com/en-us/libr...er(VS.95).aspx

    Sinon tu peux utiliser aussi un Storyboard je pense...

    http://blogs.silverlight.net/blogs/m...ame-loops.aspx

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    C'est a dire que si j'encadre mes fonctions de WebRequest et de mise a jour de l'UI avec "DispatcherTimer" cela suffira a faire communiquer mon Bakcground Thread et mon UI Thread tout en permettant a l'extérieur de ma boucle l'utilisation d'Event indépendants?

    Ephismen

  4. #4
    Membre Expert
    Avatar de Samuel Blanchard
    Homme Profil pro
    Expert .NET
    Inscrit en
    Février 2010
    Messages
    1 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France

    Informations professionnelles :
    Activité : Expert .NET

    Informations forums :
    Inscription : Février 2010
    Messages : 1 504
    Par défaut
    cela suffira a faire communiquer mon Bakcground Thread et mon UI Thread
    Oui

    tout en permettant a l'extérieur de ma boucle l'utilisation d'Event indépendants
    Je ne vois pas trop ce que tu veux dire...

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    Non mais je me rends compte que la fin de ma question n'a pas trop de sens. Bon je vais tester cette méthode et je reviendrais lorsque j'aurais réussi.

    Merci, Ephismen.

  6. #6
    Expert confirmé
    Avatar de Skyounet
    Homme Profil pro
    Software Engineer
    Inscrit en
    Mars 2005
    Messages
    6 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Software Engineer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 6 380
    Par défaut
    Sinon tu pourrais faire un WCF en polling duplex (http://msdn.microsoft.com/en-us/libr...8VS.95%29.aspx) qui enverrait des mises à jours toutes les 2 secondes.

    Sinon oui le DispatcherTime avec un délai de 2 secondes fonctionne.

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    Bonjour a tous et merci pour les réponses supplémentaires. J'ai tester le "Dispatcher.Timer" et j'ai réussi a mettre en place la boucle que je souhaitais réaliser, néanmoins je me suis confronte a un autre problème:

    Lorsque que je lance une WebRequest je dois avant tout, en Silverlight, créer un Event handler, puis m'y connecter en mode asynchrone; et le seul moyen de récupérer le résultat est de rentrer dans cette fonction et de le récupérer a travers "e.Result". Cela vaut de même pour le Dispatcher.Timer, il me crée un nouvel Event Handler, mais le problème c'est que l'on ne peux pas passer d'arguments et j'aurais besoin d'un argument précédemment obtenu dans ma nouvelle boucle. Je sais qu'il existe des moyens moyennement propre pour y parvenir, mais sachant que tout, ou presque, sera dynamique dans mon programme j'espérais trouver une méthode légère.

    Merci.

  8. #8
    Expert confirmé
    Avatar de Skyounet
    Homme Profil pro
    Software Engineer
    Inscrit en
    Mars 2005
    Messages
    6 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Software Engineer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 6 380
    Par défaut
    Tu dois t'embrouiller dans les termes parce que ce que tu dis ne veut rien dire.

    Je vais te montrer un bout de code avec un WebClient (le principe reste le même que ce soit pour un service ou autre chose).

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var client = new WebClient();
    client.DownloadStringCompleted += (o, e) => txtText.Text = e.Result;
     
    var timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromSeconds(2);
    timer.Tick += (o, e) => client.DownloadStringAsync(...);

    Code écrit sans test et sans VS.

  9. #9
    Membre Expert
    Avatar de Samuel Blanchard
    Homme Profil pro
    Expert .NET
    Inscrit en
    Février 2010
    Messages
    1 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France

    Informations professionnelles :
    Activité : Expert .NET

    Informations forums :
    Inscription : Février 2010
    Messages : 1 504
    Par défaut
    Dans son exemple Sky utilise des Lambda Expressions, ce qui lui permet de passer des paramètres dans les méthodes de ses evenements.

    http://msdn.microsoft.com/fr-fr/library/bb397687.aspx

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    Merci pour vos réponses et encore désolé pour ma question plus que confuse .
    Je pense avoir maintenant tous les éléments pour réaliser ce que je souhaitais faire.

    Merci, Ephismen.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    Bonjour,

    Après avoir tester ce que vous m'avez précédemment montrer(Dispatcher.Timer, Lambda Expression, etc...) j'ai eu de nouveaux quelques petits soucis en essayant de mettre tout cela en place. Dans l'absolu les fonctions marche parfaitement mais tout cela mis bout a bout ma créez des erreurs auxquelles je ne m'attendaient pas.


    Voici un petit schéma de ce que je fais dans mon application:

    1- Connexion au Webclient USER LOGIN qui me renvoi un GUID(user ID) unique. Je l'enregistre et je le garde car j'en ai besoin dans mes appels de Webservices suivants.

    2- Connexion au Webclient USER GETINFO en utilisant le GUID. Celui-ci me renvoi un tableau contenant dans chaque case des informations me concernant.

    3- Démarrage d'un timer ayant un tick de 2 secondes afin d'avoir une boucle.

    3b- Connexion au Webclient USER GETFRIEND, qui se situe dans mon Timer, en utilisant le GUID. Celui-ci me renvoi un tableau contenant des informations sur mes amis. Je l'ai mis dans un timer afin que ma liste d'amis se mette a jour toute les deux secondes.

    J'arrive a mettre cela en place sans trop de problèmes jusqu'à l'étape 3, mais des que je fais appel au WebClient USER GETFRIEND je suis confronte a deux problemes:

    -D'un cote je me suis aperçu que mon nombre de threads montait désespérément haut et que donc mon idée selon laquelle un appel au WebClient en asynchrone se terminait une fois les instructions effectuées était fausse.

    -Et de l'autre que lorsque j'utilisai le même "proxy"(par exemple si je déclare test.MainSoapClient proxy = new test.MainSoapClient() pour un appel au WebClient les informations de ma liste d'amis(USER GETFRIEND) venait se greffer dans celle de mon profil personnel(USER GETINFO), et que je me retrouvais donc sur mon UI(Interface Utilisateur) avec comme nom l'e-mail de mon amis.


    Ce que je souhaitais savoir c'est est-il possible de fermer un appel au WebClient que l'on utilise plus? Ou est-ce que quelqu'un aurait une toute autre suggestion sur l'organisation que mon programme devrait prendre?

    Merci d'avance.

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    Pour rajouter a ce que j'ai déjà dit plus haut je n'ai toujours pas trouver de solutions a mon problème. J'ai tenter plusieurs fois de refaire le programme en recommençant du début mais rien n'y fait. J'ai essayer avec des WebClients différents mais cela revient exactement au même, je ne sais même pas ce vers quoi je dois me tourner: soit un problème de thread ou un problème de Webclient. Si quelqu'un a la solution ou une bonne suggestion je suis preneur.

  13. #13
    Expert confirmé
    Avatar de Skyounet
    Homme Profil pro
    Software Engineer
    Inscrit en
    Mars 2005
    Messages
    6 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Software Engineer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 6 380
    Par défaut
    Moi j'ai pas répondu parce que j'ai pas compris ce que tu disais, tu parles de WebClient alors que tu utilises un SoapClient qui pointe sur un WebService.

    Donc si tu veux qu'on t'aide, explique clairement et montre nous un peu de code.

  14. #14
    Membre Expert
    Avatar de Samuel Blanchard
    Homme Profil pro
    Expert .NET
    Inscrit en
    Février 2010
    Messages
    1 504
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France

    Informations professionnelles :
    Activité : Expert .NET

    Informations forums :
    Inscription : Février 2010
    Messages : 1 504
    Par défaut
    Normalement tu devrais executer ton dispatcher comme suit dans ton evenement Tick :

    Arret du DispatcherTimer via la méthode stop.
    Appel du WS.
    puis dans le Completed du WS :
    traitement des données recues
    redemarrage du Dispatcher via Start

    L'arrêt et le redemarrage de ton Dispatcher est très important dans ton cas car tu fais des demandes courtes (2s) vers ton serveur et donc tu as des risques de lancement de plusieurs WS en parallèle !

  15. #15
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    Oui désole je n'ai pas été très précis dans les termes utilises. En effet je parle d'une classe proxy comme on en a en Ajoutant un nouveau Web service a son Projet. J'utilise donc bien un SoapClient et non pas un Webclient.

    Voici le code de mon programme:
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
     
    namespace MyProg
    {
        public partial class MainPage : UserControl
        {
            test.MainSoapClient proxy = new test.MainSoapClient();
            test.MainSoapClient friend_connect = new test.MainSoapClient();
            string guid;
     
            public MainPage()
            {
                InitializeComponent();
            }
     
     
            private void ButtonLogin_Click(object sender, RoutedEventArgs e)
            {
                test.ArrayOfString login_request_tab = new test.ArrayOfString();
     
                login_request_tab.Add(IDcontent.Text);
                login_request_tab.Add(PWcontent.Text);
     
                proxy.USER_FunctionsCompleted += new EventHandler<test.USER_FunctionsCompletedEventArgs>(proxy_USER_FunctionsCompleted);
                proxy.USER_FunctionsAsync("USER_LOGIN", login_request_tab);
            }
     
     
            void proxy_USER_FunctionsCompleted(object sender, test.USER_FunctionsCompletedEventArgs e)
            {
                test.ArrayOfString login_response = new test.ArrayOfString();
     
                login_response = (test.ArrayOfString)e.Result;
     
                if (login_response[0] == "1")
                {
                    WelcomeText.Text = "Welcome " + IDcontent.Text;
                    WelcomeText.Visibility = Visibility.Visible;
                    LoginField.Visibility = Visibility.Collapsed;
                    ButtonDisconnect.Visibility = Visibility.Visible;
                    InterfaceGrid.Visibility = Visibility.Visible;
                    guid = login_response[1];
                    Initiate_user_profile();
                 }
     
            }
     
            void Initiate_user_profile()
            {
                test.ArrayOfString request_tab_guid = new test.ArrayOfString();
     
                request_tab_guid.Add(guid);
     
                proxy.USER_FunctionsCompleted += new EventHandler<test.USER_FunctionsCompletedEventArgs>(Get_user_infos);
                proxy.USER_FunctionsAsync("USER_GETINFO", request_tab_guid);
                KeepAlive();
            }
     
            void Get_user_infos(object sender, test.USER_FunctionsCompletedEventArgs e)
            {
                test.ArrayOfString user_profile = new test.ArrayOfString();
                user_profile = e.Result;
     
                MyName.Text = user_profile[2];
                MyEmail.Text = user_profile[3];
                MyMessage.Text = user_profile[6];
            }
     
            void KeepAlive()
            {
                DispatcherTimer timer = new DispatcherTimer();
                timer.Interval = new TimeSpan(0, 0, 2);
                timer.Tick += new EventHandler(WS_refreshloop);
                timer.Start();
            }
     
            void WS_refreshloop(object sender, EventArgs e)
            {
                test.ArrayOfString request_tab_friend = new test.ArrayOfString();
     
                request_tab_friend.Add(guid);
     
                friend_connect.USER_FunctionsCompleted += new EventHandler<test.USER_FunctionsCompletedEventArgs>(Get_friend_list);
                friend_connect.USER_FunctionsAsync("USER_GETFRIENDS", request_tab_friend);
     
            }
     
            void Get_friend_list(object sender, test.USER_FunctionsCompletedEventArgs e)
            {
                test.ArrayOfString friend_list_response = new test.ArrayOfString();
                List<Friends> friend = new List<Friends>();
     
                friend_list_response = (test.ArrayOfString)e.Result;
                friend.Add(new Friends() { Friend_guid = friend_list_response[1], Name = friend_list_response[2], Message = friend_list_response[3] });
                friend.Add(new Friends() { Friend_guid = friend_list_response[4], Name = friend_list_response[5], Message = friend_list_response[6] });
                ContactList.ItemsSource = friend;
            }

    C'est un code très simple que j'ai conçu uniquement dans des buts de tests, donc je ne vérifie aucune erreur ni valeur de retour pour l'instant.

    Encore pardon pour le manque de clarté dans mes propos mais je ne dois pas encore saisir toutes les nuances en .NET sachant que je me suis mis a tout cela il y a peine 10 jours.

    Merci.

  16. #16
    Expert confirmé
    Avatar de Skyounet
    Homme Profil pro
    Software Engineer
    Inscrit en
    Mars 2005
    Messages
    6 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Software Engineer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 6 380
    Par défaut
    Et quel est ton soucis exactement ?

    Déjà je vois un problème : dans WS_refreshloop tu t'abonnes au Completed de USER_FunctionsCompleted. Le problème est que ta méthode WS_refreshloop est appelée toutes les 2 secondes donc tu t'abonnes encore et encore, donc a chaque fois que tu appelles USER_FunctionsAsync ta callback est appelée autant de fois que ta méthode a été appelée avant.

    Pour changer ça tu peux mettre les abonnements à tes Completed dans une méthode qui n'est appelé qu'une seule fois.
    Ou alors avant le += tu mets un -=

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    friend_connect.USER_FunctionsCompleted -= new EventHandler<test.USER_FunctionsCompletedEventArgs>(Get_friend_list);
    friend_connect.USER_FunctionsCompleted += new EventHandler<test.USER_FunctionsCompletedEventArgs>(Get_friend_list);

  17. #17
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    En fait mon soucis majeur viens de l'extraction des données dans e.Result.

    La fonction: void Get_user_infos(object sender, test.USER_FunctionsCompletedEventArgs e) sert a récupérer des donnees concernant l'utilisateur qui vient de se loguer.

    MyName.Text = user_profile[2];
    MyEmail.Text = user_profile[3];
    MyMessage.Text = user_profile[6];

    Et la fonction void Get_friend_list(object sender, test.USER_FunctionsCompletedEventArgs e) sert elle a récupérer des informations concernant les amis de l'utilisateur.

    Il se trouve que lorsque je fais appel a ma deuxième fonction, la première, comme je ne la fermais pas, étais toujours en "écoute" et elle récupérait donc le e.Result de ma deuxième. Ce qui faisais qu'elle lisait toujours au même endroit dans le tableau mais pas dans le bon tableau! je me retrouvais avec l'e-mail de mes amis en tant que nom d'utilisateur.

    J'espère avoir été assez clair.

    Merci.

  18. #18
    Expert confirmé
    Avatar de Skyounet
    Homme Profil pro
    Software Engineer
    Inscrit en
    Mars 2005
    Messages
    6 380
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Software Engineer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 6 380
    Par défaut
    C'est juste que tu ne te désabonnes pas des évènements.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    proxy.USER_FunctionsCompleted += new EventHandler<test.USER_FunctionsCompletedEventArgs>(proxy_USER_FunctionsCompleted);
    proxy.USER_FunctionsAsync("USER_LOGIN", login_request_tab);
     
    proxy.USER_FunctionsCompleted += new EventHandler<test.USER_FunctionsCompletedEventArgs>(Get_user_infos);
    proxy.USER_FunctionsAsync("USER_GETINFO", request_tab_guid);
    Ca te choque pas ça ?

    Et pourquoi tu crées pas une méthode User_Login et une méthode User_GetInfo à la place ? Ce serait beaucoup plus facile et maintenable.

    Si tu tiens a rester comme ça il faut que dans proxy_USER_FunctionsCompleted tu fasses

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    proxy.USER_FunctionsCompleted -= new EventHandler<test.USER_FunctionsCompletedEventArgs>(proxy_USER_FunctionsCompleted);
    Et dans Get_user_infos

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    proxy.USER_FunctionsCompleted -= new EventHandler<test.USER_FunctionsCompletedEventArgs>(Get_user_infos);

  19. #19
    Membre averti
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 43
    Par défaut
    D'accord merci je vais essayer! J'avais tout chercher a ce propos mais je n'avais pas trouver cette méthode. Car CancelAsync() et Dispose() servent a de tout autres cas.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Tri multi-threadé
    Par Tifauv' dans le forum C
    Réponses: 8
    Dernier message: 28/06/2007, 09h00
  2. Réponses: 2
    Dernier message: 15/05/2004, 18h33
  3. Réponses: 16
    Dernier message: 30/01/2004, 11h05
  4. [VB6][active x] faire du multi-thread avec vb
    Par pecheur dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 20/05/2003, 12h01
  5. [Kylix] exception qtinft.dll et multi-threading
    Par leclaudio25 dans le forum EDI
    Réponses: 3
    Dernier message: 27/03/2003, 18h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo