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:
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.