Making asynchronous service calls from a WinForm client
It’s well known that long blocking calls in the main user interface (UI) thread of a WinForm client application should be avoided. As users, we certainly get annoyed when the application stops responding. Yet, even today, we see this over-and-over again in so many applications. When making a call to a service the amount of time the call takes to complete can be unpredictable. Even if your service responds immediately, you have the potential for network latency, authentication hand-shaking, etc. to extend the time for a complete end-to-end call. Calling services in an asynchronous fashion solves this problem. In this post I’m going to show an easy way to make an asynchronous call to a service and how to use some user interface (UI) best practices for handling the result of such a call.
I’m going to start with just an out-of-the-box WCF service using the WCF Service Library template and make one small change (sleep for 5 seconds) in the GetData method to simulate a long running call.
Next, I’ll create a very basic WinForm client project. The UI for this client will allow me to select data to send to the GetData method and will show the results in the ListBox control. There are two buttons that I will use to invoke the service; one for a synchronous call and another for an asynchronous call.
I also added a NotifyIcon control to my form which you don’t see. This is what I’m going to use to notify the user that the call has completed. It will display a notification in the Windows taskbar (much like the way Windows Update notifies you when updates are available for Windows).
Since I’m using the NotifyIcon, I’ll go ahead and add an icon as a resource to the WinForm client project. You can do this easily by double-clicking the Resources node in Solution Explorer.
This will open the Resource Designer where you can select to add a new icon from the toolbar.
The icon designer will appear and you can design your icon(s) as I’ve done here.
Once that is complete, simply set the Icon property in your NotifyIcon to the icon you created. You may also want to set the Balloon Tip Title like I’ve done here or you can set it at runtime later if you prefer.
When I add my service reference I’ll be using an “Advanced” option to generate asynchronous methods to call to the service. This feature is available if you click on the Advanced… button in the Add Service Reference dialog.
When you do this, you will be given the opportunity to have SvcUtil generate asynchronous operations for you as part of the client proxy. IMO, this option should be checked anyway and not tucked away behind an “Advanced” button, but that’s just me…
I’ll start by adding the code for the synchronous call to the service behind the “Call Service Synchronously” button.
When you run the client and press the button you will eventually (after about 5 seconds) see the results displayed in the ListBox control. If during this time you try to move the form or interact with other controls on the form you won’t be able to because the client UI will be blocked while the call completes.
A solution to this annoying situation is just to call the service asynchronously as shown here in the code behind the “Call Service Asynchronously” button. The work done by svcutil.exe when generating the proxy makes this a trivial task using the GetDataAsync method to call our service and the GetDataCompleted event handler to retrieve the results.
All that remains now is to use the NotifyIcon control to notify the user when the call has completed. If we didn’t encounter an error then I’m going to make the balloon tip icon an “information” icon and show the result in the text. I’ll also add it to the ListBox control on our main form. Otherwise, I’ll show an “error” icon and the error message returned.
Finally, I’m going to wire up the click event for the icon itself where I will stop showing the icon in the taskbar and bring focus to the main form. You may want to do the same for the BalloonTipClicked event, or associate a ContextMenu with the control and drive other behaviors from it. The possibilities are abundant for you to explore.
The last thing to do now is test it. I’m going to run my application, invoke the service asynchronously. In doing so, I will still be able to interact with the form, move it around on my desktop, etc. because the main UI thread is not blocked.
When the results are ready, I will get a nice notification in the taskbar like this and the data will be in my ListBox on my main form. After 10 seconds the balloon tip will go away but the icon will stay in the taskbar until I click on it. At which time, the main form will be activated and the notification icon disabled.