Mika
Mika

Reputation: 111

System.Net.WebClient Problems

I am writing a program that has to access a MySQL database. To do this I wrote a PHP WebService to get all the values in a table. The problem I am having is that I am writing a function in C# to get those values. I want the function to wait until a event handler on the DownloadStringCompleted event is called, then return the values. I did it like this:

WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(DownloadCompletedHandler);
client.DownloadStringAsync(new Uri(Application.Current.Host.Source.AbsoluteUri + "\\PHP\\GetAdmins.php"));
while (DownloadCompleted == false) { }
return DownloadResult;

But that makes the program hang.

I need something that will make that part of the program pause until DownloadConpleted = true.

I am running Visual Studio Ultimate 2010, I am making a Silverlight application, and any help will be appreciated.

Upvotes: 1

Views: 2188

Answers (2)

Darin Dimitrov
Darin Dimitrov

Reputation: 1038770

You could use a ManualResetEvent:

public string Download()
{
    var manualEvent = new ManualResetEvent(false);
    WebClient client = new WebClient();
    var result = string.Empty;
    client.DownloadStringCompleted += (sender, e) =>
    {
        if (e.Error != null)
        {
            result = e.Result;
        }
        manualEvent.Set();
    };
    client.DownloadStringAsync(new Uri(Application.Current.Host.Source.AbsoluteUri + "\\PHP\\GetAdmins.php"));
    // block while the download is completed and the event is signaled or
    // timeout after 30 seconds
    if (!manualEvent.WaitOne(TimeSpan.FromSeconds(30)))
    {
        // timeout
    }
    return result;
}

Note that blocking the main thread is a bad practice because it will freeze the UI. A better way would be to simply handle the results in the download completed event handler. And because this handler is executed on a thread different than the UI thread don't forget to marshal any calls that update the UI to the main thread.

Recommended example:

public void string Download()
{
    var manualEvent = new ManualResetEvent(false);
    WebClient client = new WebClient();
    client.DownloadStringCompleted += (sender, e) =>
    {
        if (e.Error != null)
        {
            Dispatcher.BeginInvoke(() => 
            { 
                ResultLabel.Text = e.Result; 
            });
        } 
        else 
        {
            Dispatcher.BeginInvoke(() => 
            { 
                ResultLabel.Text = e.Error.ToString(); 
            });
        }
    };
    var url = new Uri(Application.Current.Host.Source.AbsoluteUri + "\\PHP\\GetAdmins.php");
    client.DownloadStringAsync(url);
}

This way you are no longer blocking the main thread and the UI remains fluid. Once the asynchronous operation completes you update the UI with the result by marshalling the call to the UI thread using the Dispatcher.BeginInvoke method.

Upvotes: 2

BrokenGlass
BrokenGlass

Reputation: 160882

You could use a ManualResetEvent - signaling the completion event from the download completion handler and making the main thread wait on it:

ManualResetEvent completionEvent = new ManualResetEvent(false);
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted +=delegate(object sender, DownloadStringCompletedEventArgs e)
{
    completionEvent.Set();
};

webClient.DownloadStringAsync(new Uri(Application.Current.Host.Source.AbsoluteUri + "\\PHP\\GetAdmins.php"));
completionEvent.WaitOne();

This answers your question, but it still will make the UI thread wait - you really have to embrace async with Silverlight, so work towards a solution that updates your model in the DownloadStringCompleted event handler - that way you don't have to use crutches and your application will perform much better.

Upvotes: 3

Related Questions