Binding, lookups and BusyIndicator

BusyIndicator is your friend

Let me begin by stating the obvious: if you bind your UI to one or more DDSs (DomainDataSources) then when the UI renders it will fetch values from those DDSs. Now let me ask you a question: if you hover the mouse over a grid, will that cause data to be fetched? You can guess that it does, from the mere fact that I’ve posed the question. But how can this be?

With the default stylesheet, and indeed most stylesheets, the DataGrid and other widgets are thoroughly littered with triggers that respond to mouse activity. This of course is what makes Silverlight the out-of-box multimedia extravaganza that it is, and we wouldn’t have it any other way. But it does mean that the mere proximity of a mouse pointer triggers rendering, which in turn fetches data. If the DDS involved isn’t ready this can produce unsightly exceptions that quite spoil the UX.

So what’s a girl to do?

Use a BusyIndicator, that’s what.

I can’t think of a scenario in which you wouldn’t add a BusyIndicator as the outermost widget in your Visual Tree. If you don’t do binding then it’s harmless, and if you should plonk a data-bound widget on your page or window then it’s there waiting to monitor IsBusy on any and every DDS that becomes its child, preventing user interaction until they become ready.

This disabling of UI until all the DDSs are ready sorts out a variety of issues:

  • It no longer matters in what order DDSs become ready, because combo-boxes and other Selector derivatives won’t try to fetch from lookups till they render.
  • Mouse activity no longer triggers UI activity cascading into premature fetches.

What happened to AutoBind?

AutoBind, for the great unwashed, is or rather was a Boolean property which when set to True would cause the BusyIndicator to find all its child DDSs and behave as though IsBusy were bound to the OR of all the DDS IsBusy properties, so any DSS becoming busy would lock the UI.

Sound good? According to this blog post, AutoBind was removed for performance reasons.

A workaround

Here’s how to efficiently provide the same behaviour using very little code.

First, synthesise a value from your various DDSs by creating a page property.

  172     public bool IsBusy

  173     {

  174       get

  175       {

  176         return assetDomainDataSource.IsBusy

  177           || assetGroupDomainDataSource.IsBusy

  178           || contactDomainDataSource.IsBusy

  179           || deviceDomainDataSource.IsBusy

  180           || employeeDomainDataSource.IsBusy

  181           || employeeGroupDomainDataSource.IsBusy;

  182       }

  183     }

Notice that each DDS already has an event handler for its _LoadedData event. You don’t need five identical handlers. Delete all of them and paste this instead.

  156     private void LoadingData(object sender, LoadingDataEventArgs e)

  157     {

  158       busyIndicator1.IsBusy = IsBusy;

  159     }

  160 

  161     private void LoadedData(object sender, LoadedDataEventArgs e)

  162     {

  163       busyIndicator1.IsBusy = IsBusy;

  164       textBoxSearchTerm.Focus();

  165       if (e.HasError)

  166       {

  167         System.Windows.MessageBox.Show(e.Error.ToString(), "Load Error", System.Windows.MessageBoxButton.OK);

  168         e.MarkErrorAsHandled();

  169       }

  170     }

Now go through your XAML and fix up your DDSs so they refer to the two handlers you just pasted in. The quickest way to do this is to use Edit/Replace with wildcards like this:

image

I suggest you also gather all your RIA declarations together, and put them in load dependency order. This is not strictly necessary, but it’s easier to check whether you have everything when the code is orderly.

If you try to run this it will probably barf on this line of code.

  164       textBoxSearchTerm.Focus();

Your page probably doesn’t contain a TextBox named textBoxSearchTerm. I left this in to point out that if there’s a control you’d like to focus when the data finishes updating then this is a pretty good spot to do the deed. Just remove the whole line if you don’t want to interfere with focus.

How it works

Whenever a DDS starts loading data, the page IsBusy property will return true. With subsequent load starts this property will continue to return true. When a DDS finishes loading, the page IsBusy will only return false if none of the DDSs are busy, so the BusyIndicator will remain visible until the last DDS finishes loading – exactly the desired behaviour, completely event driven and with less code than the designer generates.

I’d like to see the designer do this automatically, but I doubt the VS team would consider it. Perhaps someone with experience creating Visual Studio Addins might help.

Published 01-21-2010 13:36 by peterw

Leave a Comment

(required) 
(required) 
(optional)
(required)