WPF status bar updates - help, I seem to be spinning in circles
I seem to be all around.
I have a WPF application that has a main ribbon window with a status bar. When you go to "view", the custom control is displayed as the content of the main window.
The View has a ViewModel that handles retrieving data from the database, and the View datacontext is in the ViewModel.
I want a long running operation (fetching data) to run on a background thread, and while it triggers a status in the main window to report it accordingly. When the background task is complete, the status should return to Ready (much the same as Visual Studio).
How do I tie this together so that I can get the data access code highlighted in the ViewModel while maintaining a responsive interface?
I tried to use BackgroundWorker, these are different places in the code and I still get an unresponsive UI.
source to share
I found that the BackgroundWorker interface is awkward for this purpose, so I prefer to use ThreadPool directly.
Here's the basic idea:
public class MyViewModel
{
public SomeCollectionType<Widget> Widgets
{
get
{
if(!WidgetFillQueued)
{
WidgetFillQueued = true;
ThreadPool.QueueUserWorkItem(_ =>
{
WidgetLoadStatus = 0;
int totalCount = FetchWidgetCount();
while(_internalWidgetCollection.Count < totalCount && !AbortFill)
{
_internalWidgetCollection.AddRange(FetchMoreWidgets());
WidgetLoadStatus = (double)_internalWidgetCollection.Count / totalCount;
}
WidgetLoadStatus = null; // To indicate complete
});
}
return _internalWidgetCollection;
}
}
}
Assuming the WidgetLoadStatus is a UI associated DependencyProperty, the WPF binding system takes care of the thread transitions required to update the display of the row state.
Assuming _internalWidgetCollection allows multithreading and implements INotifyPropertyChanged properly, any updates to the collection will also update the UI on the UI thread.
There are many collections in my real view models, so instead of using separate properties such as WidgetFillQueued and WidgetLoadStatus, I use a data structure that keeps track of all currently performed operations and calculates the combined status value to display. But the above code gives a basic idea of how streaming can be properly implemented.
The above also applies to loading a single LOB like file upload: instead of calling AddRange () each time, just accumulate the data until it's loaded, then set the property that contains the data. Note that if the object itself includes DispatcherObjects, it must be deserialized on the UI thread. Do this by calling Dispatcher.BeginInvoke from the thread.
source to share