How can I display a loading control while a process is waiting for be finished?
I decided to use this third-party component to make a simple loading control in my windows form.
http://www.codeproject.com/Articles/14841/How-to-write-a-loading-circle-animation-in-NET
This works fine when turns on and off changing the property "Active" to true or false in a single request (one per time). The problem is when a process is waiting to be served, and I pretend to Active the loadingControl before the process starts and turn off when I "think" that the process has to be finished. When I do it, the image loading is shown as a static image. (Without animation).
I'm sorry for this question, I'm new in C#. But I think that I need to use Threads or something similar.
So my general code is this:
using [libraries here]...;
namespace [namespace here]
{
Public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.loadingCircle1.Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(showLoading));
this.loadingCircle1.Visible = true;
t.Start();
//Import an Excel
t.Abort();
}
public void showLoading()
{
loadingCircle1.Active = true;
loadingCircle1.RotationSpeed = 10;
}
}
}
But Always the Loading shows as a static image without the animation.
You create a thread, which simply sets two properties and then ends. The t.Abort
will probably do nothing, since the thread will have been exited by that time. Even worse, you import the excel file on the UI thread, which blocks any animation and freezes the complete UI.
This is how you should make it:
Remark: Of course if your form is responsive, you must disable/enable the controls and prepare to the case what happens if your form is being closed during the load.
1. Using threads
If you really want to explicitly use threads, do it like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Thread workerThread = null;
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation (I used a progress bar, start your circle here)
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job and the timer, which polls the thread
btnImport.Enabled = false;
workerThread = new Thread(LoadExcel);
workerThread.Start();
timer1.Interval = 100;
timer1.Start();
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
private void timer1_Tick(object sender, EventArgs e)
{
if (workerThread == null)
{
timer1.Stop();
return;
}
// still works: exiting
if (workerThread.IsAlive)
return;
// finished
btnImport.Enabled = true;
timer1.Stop();
progressBar1.Visible = false;
workerThread = null;
}
}
2. Background worker
The BackgroundWorker
can throw an event when it is finished:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += BackgroundWorker1_DoWork;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
}
private void btnImport_Click(object sender, EventArgs e)
{
// start the animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// start the job
btnImport.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
LoadExcel();
}
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
3. Using async-await
This is the simplest one.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void btnImport_Click(object sender, EventArgs e)
{
// start the waiting animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
// simply start and await the loading task
btnImport.Enabled = false;
await Task.Run(() => LoadExcel());
// re-enable things
btnImport.Enabled = true;
progressBar1.Visible = false;
}
private void LoadExcel()
{
// some work takes 5 sec
Thread.Sleep(5000);
}
}
I'd recommend to use async/await (for C# 5.0):
private void button1_Click(object sender, EventArgs e){
ImportAsync();
}
private async Task ImportAsync(){
// UI-thread
showLoading();
this.loadingCircle1.Visible = true;
// wait until task will be finished
await Task.Run(() => {
// different non-blocking thread for all the hard work, but without UI-stuff
// import an Excel
});
// going back to UI-thread
this.loadingCircle1.Visible = false;
}