Silverlight, ASP .NET, C#, AJAX, and all things Web 2.0

maandag 18 mei 2009

Silverlight: problems with async calls, and how to avoid 'em

A few weeks ago, a client asked me to come in to have a look at their Silverlight-application, since they were facing some problems with the asynchronous calls to services.  In particular: how do you go about blocking your UI when such a call is being made?  Mind you, I don't mean the type of "blocking" that hangs the UI, I mean the type that blocks the user from continuing or entering invalid info :-)

As I wrote a while ago: when you face problems like this, DO NOT try to attempt to make the service call behave as if it were synchronous.  Why?  Well, in short, it never rrrreeallly works, and it kind of defeats the purpose of using async calls to avoid an application that feels like it's hanging :-)

So, what can you do?  What if you have, for example, a button on your application which should not execute it's handler unless a few conditions are true, and what if one of those conditions is validated through a service?  Say you have a textbox, and depending on the value inputted in that textbox, the button should be clickable or not.  But: it takes some time to return from the async call checking the value in the textbox, so a fast user just might be able to click your button, even though the value in the textbox validates to false, meaning the button shouldn't be clickable.  In a non-async world, you could simply put your validation rules behind the button click, but in the async SL-world, this isn't an option anymore - if one of the validation rules requires a service call, you're stuck.

Don't fear though :-)  A few really simple rules apply to make sure your async calls won't pose problems when concerning user input, validation, etc.  This post will outline (some of) 'em, and provide you with a downloadable example project at the end of this post.

  1. the first one: change the way you handle your validation: instead of assuming something is valid until proven otherwise, assume it's invalid until proven otherwise.  Eg, if a button should only be clickable if every input field is valid, disable it by default, and only enable it after every field has been validated to true.  You might want to add handlers for validation to the lostfocus-event of your input fields, for example. 

  2. a second rule: do not execute async server side validation on the click of the button which defines wether or not the user can continue with the application.  Validate when you can, when it's needed, not at the last possible moment: otherwise, you might run into the problem where you want to execute code depending on what value is returned from your async call, but you have no way of telling WHEN it will return.  Therefore, I like to validate my fields on, for example, their lostfocus-event.

  3. third one: when validating or doing a server-side call, show this to your user.  Show a loading-icon or progress indicator, so the user won't start yelling at his computer screen, because the value he inputted in the textbox is valid, yet the button to continue is still greyed out.

  4. fourth one: disabling input, clicks, et cetera can be done in various ways: removing handlers, disabling a visual or setting the IsHitTestVisible-property to true could all be considered.

  5. fifth one, not a rule, rather a suggestion: to avoid hammering, you might want to consider to "block" your application from starting too much service calls when validating.  You can do this by overlaying your application with a stretched canvas while the service call is being made (and consequently remove the overlay when the call returns).  This, however, should be handled with care, 'cause using it too much might give the impression of a non-responsive app.

A small example project using a few of these techniques can be found here.  Hope this helps some of you out! :-)