How to Display a Progress Dialog Bar while Loading Data in C# .NET
Quick Start Guide | |
---|---|
What You Will Need |
Visual Studio 2022 |
Controls Referenced | |
Tutorial Concept | How to use progress bars asynchronously in .NET applications and customize the progress bars for different styles and scenarios. |
When you're designing your application, you need to consider how to handle data loading in order to ensure the application always remains responsive to the user. Generally, this is done by loading data asynchronously on a background thread, while the user may continue to interact with the application.
The issue is that without proper visualization, the user may wonder whether anything is happening. This leads to the use of a progress bar to indicate that some activity is being executed in the background.
Once the data has been loaded, the progress bar should be hidden, and the relevant controls to display the data should be shown.
In this blog, we will show a few examples of working with progress bars for WinForms and WPF with asynchronous data loading and show how to accomplish it with a non-asynchronous process. We will explore:
- Indeterminate Progress Bars for Asynchronous Operations
- Continuous Progress Bars
- How to Make Anything Asynchronous
- Custom Progress Bars for WPF
Indeterminate Progress Bars for Asynchronous Operations
The first step is to asynchronously load your data so that the application UI remains responsive during longer-than-usual load times. This is handled pretty easily in .NET using an awaited method call. It means that any code that happens after the await, will not run until the operation is complete. It’s the easiest solution, as shown below.
private async Task LoadData()
{
// show progress bar
progressBar1.Visible = true;
await LoadDataAsync();
// hide progress bar
progressBar1.Visible = false;
}
All you have to do is set the progress bar visibility before you await the loading of data, and then set it invisible when the operation is complete. This works well if the progress bar is “indeterminate”, which means it will keep looping through the loading animation indefinitely until you stop it.
It’s a common design paradigm today to display an indeterminate progress bar, or ring, as data is loading. This is popular because, with unpredictable networks, we don’t know exactly how long it may take. The server we are calling may not be capable or willing to inform us how long it will take either.
To create an indeterminate progress bar in WinForms, set its Style property to Marquee. In WPF, set its IsIndeterminate property to True.
Continuous Progress Bars
The downside to indeterminate progress bars is that the user doesn’t know how much time is remaining. The alternative is to report progress to the progress bar as your data is loaded. This approach is popular for installing software so that the user knows exactly how much progress (and sometimes how much time as well) is remaining. This may be possible to use during data load as well, depending on your data solution.
Let’s take a look at a code example that uses a background worker, rather than awaited code so that we have complete control over updating the progress bar.
BackgroundWorker worker = new BackgroundWorker();
public App()
{
InitializeComponent();
// set background worker
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.WorkerReportsProgress = true;
}
To set up the background worker, we must listen to the ProgressChanged, RunWorkerCompleted, and DoWork events.
Here’s what we do in each:
- DoWork: Perform the actual work of your application (such as loading data) and, wherever possible, report progress to the progress bar using its ReportProgress method. This will trigger the ProgressChanged event. If loading data, try to load it in chunks and report progress between.
- ProgressChanged: Update the actual value of the progress bar. This event accepts a parameter so you can pass variable amounts, depending on the workload.
- RunWorkerCompleted: Finish with the progress, such as hide the progress bar or display a message to the user.
To kick this whole process off, just call the RunWorkerAsync method.
// run background worker
worker.RunWorkerAsync();
To see a full example, check out the “Performance” FlexGrid demo in our WinForms Control Explorer.
Ready to Check it Out? Download ComponentOne Today!
How to Make Anything Asynchronous
The above examples work well if your data is already being loaded asynchronously. Having the data load asynchronously, or on a background thread, is the key to displaying progress indication to the user. But not everything is supported through async methods like above, especially things like updating UI controls because all of that will happen on the UI thread.
Below are two examples of how you can make non-asynchronous code into asynchronous code. The first example takes advantage of the IAsyncAction interface. This allows you to do some code without a return object. Dispatcher is used to access our UI controls on another thread.
public IAsyncAction LoadSomethingAsync()
{
return Task.Run(() =>
{
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
// do stuff to your UI
});
}).AsAsyncAction();
}
The code above is not as pretty, but it makes the method call very crisp and simple. Just use the await keyword before the call, and now it will run asynchronously. You can test this by activating a progressRing before, and it will continue to spin while the long operation continues.progressRing.IsActive = true;
await LoadSomethingAsync();
progressRing.IsActive = false;
The second approach is a bit fancier as it takes advantage of the ThreadPool class to queue our computationally intensive work while not blocking the UI thread during its run. This time, we use the async keyword on our method, which enables us to await the Dispatcher call.public async void LoadSomething()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
// do stuff to your UI
});
}
progressRing.IsActive = true;
await ThreadPool.RunAsync(delegate { LoadSomething(); });
progressRing.IsActive = false;
Custom Progress Bars for WPF
ComponentOne provides custom progress bar controls for WPF. The C1ProgressBar control is modeled after the native progress indicators used on Windows 8-10 to provide a modern user experience. It can display as an animated looping pattern of dots to indicate that an indeterminate operation is in progress. Also, the C1ProgressIndicator control shows a loading ring.
You can download the C1ProgressBar and C1ProgressIndicator controls as part of the C1.WPF core library.
Ready to Get Started? Download a FREE 30-Day Trial of ComponentOne Today!