Below is the code for a C# **async** method that also has callback handlers for two of the **WebClient** control's events: **DownloadProgressChanged** and **OpenReadCompleted**. When I run the code, initially it flows to the "*await DownloadStringTaskAsync()*" call and exits. Then I see the anonymous event handler code fire for **DownloadProgressChanged**, and this is where I have a problem. The code then flows to the "*return strRet*" statement so the return for the method is the initialization value "(none)" assigned to **strRet** at the top of the method, instead of the contents of the web page that is assigned to **strRet** in the **OpenReadCompleted** anonymous callback. 

So I need to wait for the **OpenReadCompleted** callback to execute before control flows to the return statement, but I'm not sure how to do this properly. How do I correct the code so that it does not reach the "*return strRet*" statement until the **OpenReadCompleted** callback has executed?

```
    /// <summary>
    /// This method downloads the contents of a URL to a string.  Returns the URL contents
    ///  as a string if it succeeds, throws an Exception if not.
    /// <param name="strUrl">The URL to download.</param>
    /// <param name="progress">An IProgress object to report download progress to.  May be NULL.</param>
    /// <param name="cancelToken">A cancellation token. May be NULL.</param>
    /// <param name="iNumSecondsToWait">The number of seconds to wait before cancelling the download. Default is 30 seconds</param>
    /// </summary>
    /// <remarks>
    /// Use "await" with this method wrapped in Task.run() to manage the process asynchronously.
    /// 
    /// NOTE: The DownloadProgressChanged() event is raised on the UI
    ///  thread so it is safe to do UI updates from the IProgress.Report()
    ///  method.
    /// </remarks>
    async public static Task<string> URLToString(string strUrl, IProgress<int> progress, CancellationToken cancelToken, int iNumSecondsToWait = 30)
    {
        // The string to be returned.
        string strRet = "(none)";

        strUrl = strUrl.Trim();

        if (String.IsNullOrWhiteSpace(strUrl))
            throw new ArgumentException("(Misc::URLToString) The URL is empty.");

        if (iNumSecondsToWait < 1)
            throw new ArgumentException("(Misc::URLToString) The number of seconds to wait is less than 1.");

        // Asynchronous download.  Note, the Silverlight version of WebClient does *not* implement 
        //  IDisposable.
        WebClient wc = new WebClient();

        // Create a download progress changed handler so we can pass on progress
        //  reports to the caller if they provided a progress report object.
        //  This event is raised on the UI thread.
        wc.DownloadProgressChanged += (s, e) =>
        {
            // Do we have a progress report handler?
            if (progress != null)
                // Yes, call it.
                progress.Report(e.ProgressPercentage);

            // If we have a cancellation token and the operation was cancelled, then abort the download.
            if (cancelToken != null)
                cancelToken.ThrowIfCancellationRequested();

        }; // wc.DownloadProgressChanged += (s, e) =>

        //  Use a Lambda expression for the "completed" handler
        //  that writes the downloaded contents as a string to a file.
        wc.OpenReadCompleted += (s, e) =>
        {
            // If we have a cancellation token and the operation was cancelled, then abort the download.
            if (cancelToken != null)
                cancelToken.ThrowIfCancellationRequested();

            // Return the downloaded file as a string.
            strRet = e.Result.ToString();
        }; // wc.OpenReadCompleted += (s, e) =>

        // Now make the call to download the file and do an asynchronous wait for the result.
        await wc.DownloadStringTaskAsync(new Uri(strUrl));

        // wc.DownloadStringAsync(new Uri(strUrl));

        return strRet;
    } // async public static void URLToStr

```

================================

**UPDATE:** Based on the answers I received I have modified the code to the following:

```
    async public static Task<string> URLToStringAsync(string strUrl, IProgress<int> progress, CancellationToken cancelToken, int iNumSecondsToWait = 30)
    {
        strUrl = strUrl.Trim();

        if (String.IsNullOrWhiteSpace(strUrl))
            throw new ArgumentException("(Misc::URLToStringAsync) The URL is empty.");

        if (iNumSecondsToWait < 1)
            throw new ArgumentException("(Misc::URLToStringAsync) The number of seconds to wait is less than 1.");

        // Asynchronous download.  Note, the Silverlight version of WebClient does *not* implement 
        //  IDisposable.
        WebClient wc = new WebClient();

        // Create a download progress changed handler so we can pass on progress
        //  reports to the caller if they provided a progress report object.
        //  This event is raised on the UI thread.
        wc.DownloadProgressChanged += (s, e) =>
        {
            // Do we have a progress report handler?
            if (progress != null)
                // Yes, call it.
                progress.Report(e.ProgressPercentage);

            // If we have a cancellation token and the operation was cancelled, then abort the download.
            if (safeCancellationCheck(cancelToken))
                wc.CancelAsync();
        }; // wc.DownloadProgressChanged += (s, e) =>

        // Now make the call to download the file and do an asynchronous wait for the result.
        return await wc.DownloadStringTaskAsync(new Uri(strUrl));
    } // async public static void URLToStringAsync

```