How to download files in a blocking/synchronous manner?
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 DownloadPr开发者_运维百科ogressChangedEventHandler(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
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.
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));
}
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.
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.";
}
精彩评论