views:

450

answers:

4

Hi, I am pretty new to silverlight and was very surprised to see that only asynchronous file downloading can be done. Well, I've attempted to counter act this by just setting a flag and waiting on it to change.. This is my simple code

 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;

 }

This is causing Firefox to actually freeze up(which is extremely annoying when I'm doing other things while developing) (btw, kudos to firefox cause I tested it and firefox froze, but I didn't lose what I was typing here after restoring)

I don't understand why the while(XmlStateStream==null){} is causing a freeze up. Is there some attribute for locks or volatile(other than what I already have) or am I in the wrong part of the Silverlight page lifecycle or something?

I'm really confused as to why this is not working.

Also, this is silverlight 3.0

+4  A: 

Most likely, this code is running in the UI thread that handles all of the web browser's interaction with the user. This is why you won't find any blocking operations - because anything that blocks will freeze the UI in exactly the same way that you saw! What's more, if the UI thread also handles network IO (which is common), then you'll deadlock here because the asynchronous operation you're waiting on will never finish.

I'm afraid you'll just have to rewrite your code as a state machine driven by asynchronous operations.

bdonlan
A: 

You need to use the DownloadFileCompleted event.

delete this:

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

add this:

webClient.DownloadFileCompleted +=
    new DownloadFileCompletedEventHandler(webClient_DownloadFileCompleted);

and this:

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

If you really must have synchronous downloading, you need to "poll" for the download being complete less often. Try calling System.Threading.Thread.Sleep() with a delay of 50-250ms from within your busy-wait loop.

Although this will reduce the wasteful CPU utilization of your code, it's possible that it will not fix the UI responsiveness problem. It depends on whether the thread that's calling your MainPage_Loaded is the only one that can call UI update events. If that's the case, then the UI simply can not update until that handler returns.

Tim Sylvester
That's not downloading a synchronous manner, though, is it now? I suspect the OP's real code is a lot more complex than the example here... :/
bdonlan
Nevertheless, it's what the OP needs to do. Actually I suspect that a synchronous request is literally impossible because the download may be happening in another thread. Regardless of the complexity, it's an event-driven system and trying to write procedural, synchronous code will never work well. It may be amusing to answer "How do I shoot myself in the foot" with gun tips, but sometimes "don't" is really the best advice.
Tim Sylvester
I had just began writing the program, but it was the "natural" way for me to try to write it.. I guess things have to be a little harder so they can be more parallel/responsive though.. I will just have to adjust(even more!) to silverlight programming.. I wasn't aware that there was so much multithreading in silverlight going on behind the scenes..(or really so little since downloading is taking place in the UI thread)
Earlz
+1  A: 

Whilst you need to get with the asynchronous nature of things in Silverlight you can use C# 3 syntax to keep things a bit more together:-

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));
}
AnthonyWJones
Anonymous functions.. .hmm well thats handy I wasn't aware you could completely do that
Earlz
A: 

By blocking until the file is downloaded, you're not only blocking the UI thread of your silverlight app - you're also blocking the UI thread of the browser, it would seem.

All you really want to do (I presume) is stop your app doing anything until the download completes. Try adding this line to MainPage_Loaded:

LayoutRoot.IsHitTestVisible = false;

Remove your blocking while loop and the completed message (the last two lines).

In webClient_OpenReadCompleted, add:

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

And everything should work the way I think you want.

Here's the full code:

   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.";

    }
Jim Lynn