Silverlight: When an Async request isn’t Asynchronous

Having not done much with Silverlight I thought I’d give it a go today building a very simple application that would periodically go off to a webservice, retrieve a new item and insert it into a list.  The UI which you can see below is very basic – it has a Start and Stop button and a list which is gradually populated with items. Clicking the start button initiates the request process which periodically (approx every 2 secs) makes the web service request.

image

This all seems simple enough and you would have throught the following would work:

    Private service As SimpleServices2.SimpleService2SoapClient

    Private Sub Start_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        If service Is Nothing Then
            Dim temp = New SimpleServices2.SimpleService2SoapClient
            service = temp

            AddHandler temp.HelloWorldCompleted, AddressOf SimpleCallBack
            temp.HelloWorldAsync()
        End If
    End Sub

    Private Sub SimpleCallBack(ByVal sender As Object, ByVal e As SimpleServices2.HelloWorldCompletedEventArgs)
        Static count As Integer
        count += 1
        Me.mItemList.Items.Insert(0, e.Result & " " & count.ToString)

System.Threading.Thread.Sleep(2000)
Dim temp = service If temp Is Nothing Then Return temp.HelloWorldAsync() End Sub Private Sub Stop_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) If service IsNot Nothing Then Dim temp = service service = Nothing RemoveHandler temp.HelloWorldCompleted, AddressOf SimpleCallBack End If End Sub

Whilst this would appear to work the UI appears to be very unresponsive.  Sure enough if you put a breakpoint in you will see that SimpleCallBack gets executed on the UI thread – most unusual if you are used to WinForms programming where the call back would generally not be on the UI thread.  Ok, so the thing to do here would be to create a background thread to do the periodic web service call. 

Wrong again! Unfortunately as Simon points out there is a dependency that requires service calls to be made on the UI thread (urgh you say). So, now we have to code around this in order to jump to a background thread to sleep (so as not to block the UI thread), then jump back again to the UI thread to execute the web service request.  Of course ideally a bit of refactoring would result in us initiating a background thread that simply makes a synchronous call but unfortunately there is no synchronous method on the service proxy.

What we end up with is the following:

  Private Sub SimpleCallBack(ByVal sender As Object, ByVal e As SimpleServices2.HelloWorldCompletedEventArgs)
        Static count As Integer
        count += 1

        Me.mItemList.Items.Insert(0, e.Result & " " & count.ToString)

        Dim t As New System.Threading.Thread(AddressOf WaitThenRetrieve)
        t.Start()
    End Sub

    Private Sub WaitThenRetrieve()
        System.Threading.Thread.Sleep(2000)
        Me.mItemList.Dispatcher.BeginInvoke(New System.Threading.ThreadStart(AddressOf RetrieveNewItem))
    End Sub

    Private Sub RetrieveNewItem()
        Dim temp = service
        If temp Is Nothing Then Return
        temp.HelloWorldAsync()
    End Sub

This ain’t a great looking solution but I’ll leave it to you to work out how you can abstract this into a background thread, or better still into a wrapper class that handles the rubbish stuff for you…..Perhaps the Silverlight team might want to FIX this as it’s a royal pain at the moment.

Leave a comment