Search code examples
vb.netwebclient

How to make WebClient's DownloadString perform asynchronously?


I created a simple hello world program to download data from the web.

Private Sub cmdSurf_Click(sender As Object, e As EventArgs) Handles cmdSurf.Click
    Dim wb As New System.Net.WebClient

    Dim uri1 = New Uri(TextBox1.Text)
    Dim str = wb.DownloadString(uri1)
    TextBox2.Text = str
End Sub

It's pretty simple right. I use a WebClient object to download a string syncrhonously and then I display the result in a TextBox.

Now, I want to do so asynchronously.
Basically, after I download the URI I do something else.
Then after the download is finished, I am doing what it should be.

So I did

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Dim wb As New System.Net.WebClient

    Dim uri1 = New Uri(TextBox1.Text)
    wb.DownloadStringAsync(uri1)
    TextBox2.Text = ""
End Sub

It turns out DownloadStringAsync(uri1) is a Sub, so it doesn't return anything.
So, well, what should be displayed in TextBox2 then? What am I missing?

Update: I realized that I should have used DownloadStringAsyncTask().
So I did this:

Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    Await downloadStringAndAssignText()
    TextBox2.Text = "I am downloading this. This text will change once the download is finished"
End Sub

Private Async Function downloadStringAndAssignText() As Task
    Dim wb As New System.Net.WebClient

    Dim uri1 = New Uri(TextBox1.Text)
    Dim str = Await wb.DownloadStringTaskAsync(uri1)
    TextBox2.Text = str
End Function

This is almost correct.
The thing is I want to do this properly so that

TextBox2.Text = "I am downloading this. This text will change once..."

is called BEFORE wb.DownloadStringTaskAsync(uri1) is finished.
Also I do not want warning. So how exactly should I do that?


Solution

  • You can set the text of your TextBox before you call the async method that downloads the string. When Await DownloadStringAndAssignText() is called, control will return to the calling Thread (the UI Thread), so the TextBox.Text is set and shown.

    When the async method returns, you can assign to the same TextBox the returned string value.
    Your method should return a value, the string it downloaded, not assign a value to a UI element that doesn't belong to this method (its responsibility is to download a string): here the return type is set to Task(Of String).
    You can directly assign the return value of the async method to the same TextBox.Text property.

    The WebClient object should be declared with a Using statement, so it's disposed of when the method completes.

    You can directly return the result of WebClient.DownloadStringTaskAsync(), since this method returns a string, the same return type of your method.

    Imports System.Net
    
    Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        TextBox2.Text = "I am downloading..."
        TextBox2.Text = Await DownloadStringAndAssignText(TextBox1.Text)
    End Sub
    
    Private Async Function DownloadStringAndAssignText(url As String) As Task(Of String)
        Using wb As New WebClient()
            Return Await wb.DownloadStringTaskAsync(New Uri(url))
        End Using
    End Function