2009-11-03 9 views
2

Je suis assez nouveau à Silverlight et j'ai été très surpris de voir que seul le téléchargement de fichiers asynchrone peut être fait. Eh bien, je suis tenté de contrer agir en réglant simplement un drapeau et d'attendre là-dessus pour changer .. Ceci est mon code simpleComment télécharger des fichiers de manière bloquante/synchrone?

void MainPage_Loaded(object sender, RoutedEventArgs e) 
    { 
     WebClient webClient = new WebClient(); 
     webClient.DownloadProgressChanged += 
      new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged); 
     webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted); 
     webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative)); 
     while (XmlStateStream == null) { } 
     lblProgress.Content = "Done Loading"; 
    } 
    void webClient_DownloadProgressChanged(object sender, 
     DownloadProgressChangedEventArgs e) { 

     lblProgress.Content = "Downloading " + e.ProgressPercentage + "%"; 
    } 
    volatile Stream XmlStateStream = null; 
    void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) 
    { 
     if (e.Error != null) 
     { 
      lblProgress.Content = "Error: " + e.Error.Message; 
      return; 
     } 
     XmlStateStream = e.Result; 

    } 

Cela est à l'origine de Firefox à geler réellement (ce qui est extrêmement ennuyeux quand Je fais d'autres choses tout en développant) (btw, bravo à firefox car je l'ai testé et firefox gelé, mais je n'ai pas perdu ce que je tapais ici après la restauration)

Je ne comprends pas pourquoi le while(XmlStateStream==null){} est provoquant un gel. Y at-il un attribut pour les verrous ou volatile (autre que ce que j'ai déjà) ou suis-je dans la mauvaise partie du cycle de vie de la page Silverlight ou quelque chose?

Je suis vraiment confus quant à savoir pourquoi cela ne fonctionne pas.

En outre, c'est silverlight 3.0

+0

Lors de la programmation dans un événement interface utilisateur entraîné (. C.-à-Silverlight), vous devez toujours utiliser des méthodes asynchrones lorsque repli vous avez des charges de travail lourdes. Pourquoi devez-vous le faire de manière synchrone? –

Répondre

5

, ce code le plus probable est en cours d'exécution dans le thread d'interface utilisateur qui gère toutes les interactions du navigateur Web avec l'utilisateur. C'est pourquoi vous ne trouverez aucune opération de blocage, car tout ce qui bloque gèle l'interface utilisateur exactement comme vous l'avez vu! Qui plus est, si le thread de l'interface utilisateur gère également les E/S réseau (ce qui est courant), alors vous serez bloqué ici car l'opération asynchrone que vous attendez ne se terminera jamais.

Je crains que vous n'ayez qu'à réécrire votre code en tant que machine d'état pilotée par des opérations asynchrones.

0

Vous devez utiliser l'événement DownloadFileCompleted.

supprimer ceci:

while (XmlStateStream == null) { } 
lblProgress.Content = "Done Loading"; 

ajouter ceci:

webClient.DownloadFileCompleted += 
    new DownloadFileCompletedEventHandler(webClient_DownloadFileCompleted); 

et ceci:

void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventHandler) { 
    lblProgress.Content = "Done Loading"; 
} 

Si vous devez vraiment avoir le téléchargement synchrone, vous devez "sondage" pour la le téléchargement étant terminé moins souvent. Essayez d'appeler System.Threading.Thread.Sleep() avec un délai de 50-250ms depuis votre boucle d'attente. Bien que cela réduise l'utilisation inutile du processeur de votre code, il est possible que cela ne règle pas le problème de réactivité de l'interface utilisateur. Cela dépend si le thread qui appelle votre MainPage_Loaded est le seul à pouvoir appeler les événements de mise à jour de l'interface utilisateur. Si c'est le cas, l'interface utilisateur ne peut tout simplement pas se mettre à jour tant que ce gestionnaire n'est pas revenu.

+0

Cela ne télécharge pas une manière synchrone, cependant, est-ce maintenant? Je soupçonne que le code réel de l'OP est beaucoup plus complexe que l'exemple ici ...:/ – bdonlan

+0

Néanmoins, c'est ce que le PO doit faire. En fait, je soupçonne qu'une requête synchrone est littéralement impossible parce que le téléchargement peut se produire dans un autre thread. Indépendamment de la complexité, c'est un système piloté par les événements et essayer d'écrire du code procédural et synchrone ne fonctionnera jamais bien. Il peut être amusant de répondre "Comment me tirer dans le pied" avec des conseils d'armes à feu, mais parfois "ne pas" est vraiment le meilleur conseil. –

+0

Je venais de commencer à écrire le programme, mais c'était la façon "naturelle" pour moi d'essayer de l'écrire .. Je suppose que les choses doivent être un peu plus difficiles, donc elles peuvent être plus parallèles/réactives. pour ajuster (encore plus!) à la programmation Silverlight .. Je ne savais pas qu'il y avait tellement de multithreading dans Silverlight aller dans les coulisses .. (ou vraiment si peu que le téléchargement se déroule dans le fil de l'interface utilisateur) – Earlz

2

Alors que vous devez obtenir avec la nature asynchrone des choses dans Silverlight, vous pouvez utiliser C# 3 syntaxe pour garder les choses un peu plus ensemble: -

void MainPage_Loaded(object sender, RoutedEventArgs e) 
{ 
    DownloadXmlStateStream(); 
} 

void DownloadXmlStateStream() 
{ 
    WebClient webClient = new WebClient(); 

    webClient.DownloadProgressChanged += (s, e) => {  
     lblProgress.Content = "Downloading " + e.ProgressPercentage + "%"; 
    } 

    webClient.OpenReadCompleted += (s, e) => { 
     if (e.Error != null) 
     { 
      lblProgress.Content = "Error: " + e.Error.Message; 
     } 
     else 
     { 
      XmlStateStream = e.Result; 
      lblProgress.Content = "Done Loading"; 
     }   
    } 

    webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative)); 
} 
+0

Fonctions anonymes ..hmm c'est bien pratique, je ne savais pas que vous pouviez faire complètement cela – Earlz

0

En bloquant jusqu'à ce que le fichier est téléchargé, vous êtes non seulement bloquer le fil de l'interface utilisateur de votre application Silverlight - vous bloquez également le fil de l'interface utilisateur du navigateur, il semblerait.

Tout ce que vous voulez vraiment faire (je présume) est d'arrêter votre application à faire quoi que ce soit jusqu'à ce que le téléchargement se termine.Essayez d'ajouter cette ligne à MainPage_Loaded:

LayoutRoot.IsHitTestVisible = false; 

Retirez votre blocage en boucle et le message terminé (les deux dernières lignes).

En webClient_OpenReadCompleted, ajouter:

LayoutRoot.IsHitTestVisible = true; 
lblProgress.Content = "Done Loading."; 

Et tout devrait fonctionner de la façon dont je pense que vous voulez.

Voici le code complet:

void MainPage_Loaded(object sender, RoutedEventArgs e) 
    { 
      WebClient webClient = new WebClient(); 
      webClient.DownloadProgressChanged += 
        new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged); 
      webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted); 
      webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative)); 
      LayoutRoot.IsHitTestVisible = false; 
    } 
    void webClient_DownloadProgressChanged(object sender, 
      DownloadProgressChangedEventArgs e) { 

      lblProgress.Content = "Downloading " + e.ProgressPercentage + "%"; 
    } 

    void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) 
    { 
      if (e.Error != null) 
      { 
        lblProgress.Content = "Error: " + e.Error.Message; 
        return; 
      } 
      LayoutRoot.IsHitTestVisible = true; 
      lblProgress.Content = "Done Loading."; 

    }