Busy Indicators and Many Threads
I recently ran across this post by Brian Lagunas quite by accident whilst searching for some controls, which I happily found in the Extended WPF Toolkit. It’s almost daily now that the particular problem of presenting users with feedback about the status of some background task is necessary. For a while now the System.Threading.Tasks namespace has made it relatively easy for .NET developers to work asynchronously. Presentations at //build/ focused on a lot on the languages and tooling upcoming in the Microsoft stack that will make this even easier. At some point we may be running many background tasks to retrieve information or process data, each of which might contribute to some visual indicator to the user that the system is working. But we certainly don’t want a screen full of busy indicators cluttering the application. So what would a framework look like where many asynchronous operations might contribute to some common global indicator?
I wrote up a quick jig to simulate how this might work out. Using MVVMLight or similar framework it’s trivial to create a system where busy messages are sent when tasks start and finally complete. By responding to these messages and doing some simple counting, a single busy indicator on the UI can handle any number of background tasks.
The app supplies two buttons that simulate starting a list of tasks. In this example all they are doing in the background is waiting.
When they start start, each task sends a message indicating the IsBusy indicator should be displayed while some work is being done. So, for each button press 10 background tasks are started, and until they are all completed the busy indicator will remain displayed. The management of the busy messages is all done in the BusyIndicatorViewModel supporting the UI element.
[csharp]
public BusyIndicatorViewModel()
{
Messenger.Default.Register<BusyIndicatorMessage>(this,
(message) =>
{
ReferenceCountedBusyEnabler(message.IsBusy);
});
}
private int _busyCount = 0;
private object _sync = new object();
private void ReferenceCountedBusyEnabler(bool isBusy)
{
lock (_sync)
{
_busyCount += (isBusy ? 1 : -1);
if (_busyCount < 0)
_busyCount = 0;
Util.ExecuteViaCurrentDispatcher(() => IsBusy = _busyCount > 0);
}
}
[/csharp]
In this way any number of independent tasks can be tracked in a decoupled manner. The code is here, if you’re so inclined. ThreadingBusyIndicator.zip