I made a form that plays a progressbar role here's the code i made
public partial class PXProgressBar : Form
{
public delegate bool CancelEvent();
public event CancelEvent cancel_e;
public Boolean ProcessCancelled
{
get;
set;
}
public PXProgressBar(bool EnableCancel)
{
InitializeComponent();
ProcessCancelled = false;
progressBar1.Minimum = 0;
if (!EnableCancel)
Cancelbtn.Visible = false;
}
public void increament(int step)
{
if (progressBar1.Value < progressBar1.Maximum-1)
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
progressBar1.Refresh();
}
else
{
progressBar1.Value++;
progressBar1.Caption = progressBar1.Value.ToString() + " of " + progressBar1.Maximum;
if (this.TopMost)
this.TopMost = false;
this.Update();
this.Hide();
this.WindowState = FormWindowState.Minimized;
// this.Dispose();
}
}
public void SetMaximum(int MaximumValue)
{
if (MaximumValue <= 0)
{
progressBar1.Maximum = 0;
return;
}
if (progressBar1.Minimum != 0 && MaximumValue < progressBar1.Minimum)
{
progressBar1.Maximum = progressBar1.Minimum;
return;
}
progressBar1.Maximum = MaximumValue;
}
public void SetMinimum(int MinimumValue)
{
progressBar1.Value = 0;
if (MinimumValue <= 0)
{
progressBar1.Minimum = 0;
return;
}
if (progressBar1.Maximum != 100 && MinimumValue > progressBar1.Maximum)
{
progressBar1.Minimum = progressBar1.Maximum;
return;
}
progressBar1.Minimum= MinimumValue;
}
public void SetTitle(string ProcessTitle)
{
this.ProgressTitlelb.Text =ProcessTitle;// ProcessTitle;
//this.ProgressTitlelb.Left = (this.panel1.Width - this.ProgressTitlelb.Width) / 2;
//this.ProgressTitlelb.Top = (this.panel1.Height - this.ProgressTitlelb.Height) / 2;
this.Update();
}
private void Cancelbtn_Click(object sender, EventArgs e)
{
ProcessCancelled = true;
bool disposeRequired =cancel_e();
if(disposeRequired)
this.Dispose();
}
private void PXProgressBar_Shown(object sender, EventArgs e)
{
this.Update();
}
}
and i call the form through this code
if (ProgressBar == null)
ProgressBar = new PXProgressBar(true);
ProgressBar.SetTitle("Saving ...");
ProgressBar.SetMinimum(0);
ProgressBar.SetMaximum(100);
ProgressBar.TopMost = true;
ProgressBar.Show();
Application.DoEvents();
regarding that the past few lines are in a unction that is called throught a thread
but when i run it the form hangs so i cant set a Cancel Button in the form to let the user cancel the operation
Your code looks like it should be fine so I can only assume that you are doing a long running operation on the UI thread which would cause the UI to look like its hung. You need to perform long running operations on a background thread so that the UI thread remains responsive enough to respond to button clicks etc. There are many many articles about this if you consult your friend Google.
More info on that here http://www.idevforfun.com/index.php/2010/01/10/windows-ui-threading/
I agree with Skizz on the DoEvents call ... there's only a very few rare cases where that call is needed and mostly its in the framework itself that it gets used.
You need to make sure the GUI elements are created on the main form thread and not from a separate thread. So, you need to get you thread that is doing the work to get the main form thread to display and update the progress bar. This is going to take a bit of refactoring.
So, in your worker thread:
void DoWork () // of whatever it's called
{
main_form.CreateProgressBar ();
while (doing stuff)
{
main_form.IncrementProgressBar ();
do stuff
}
main_form.DestroyProgressBar ();
}
And in the main form:
delegate void Callback ();
void CreateProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (CreateProgressBar));
}
else
{
progress_bar = CreateProgressBar ();
}
}
void IncrementProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (IncrementProgressBar ));
}
else
{
progress_bar.IncrementProgressBar ();
}
}
void DestroyProgressBar ()
{
if (InvokeRequired)
{
Invoke (new Callback (DestroyProgressBar));
}
else
{
progress_bar.Close ();
progress_bar = null;
}
}
The InvokeRequired determines if the calling thread is the same as the GUI thread. If the calling thread is not the GUI thread, the Invoke is used to changed thread context. This is the synchronous version and won't complete until the invoked method is finished. There is an asynchronous version called BeginInvoke but this isn't really needed for what your doing.
The problem might be the DoEvents method call. From this MSDN page:
Calling this method causes the current thread to be suspended while
all waiting window messages are processed. If a message causes an
event to be triggered, then other areas of your application code may
execute. This can cause your application to exhibit unexpected
behaviors that are difficult to debug. If you perform operations or
computations that take a long time, it is often preferable to perform
those operations on a new thread. For more information about
asynchronous programming, see Asynchronous Programming Overview.
I don't think the DoEvents call is necessary. If you need to halt the code after the Show for the operation to complete, then use a System.Threading.EventWaitHandle instead.
There's some link maybe helpful for you about progressbar:
How do I implement a progress bar in C#?
Hope this help.
Just create other thread for progressbar and use it in background
Related
I have 2 form (formA & formB) in my project c#, i want to run some process in backgroundworker when i click a button in formA.
can i update from backgroundworker to label in formB?
here's the code in formA
private void button1_Click_1(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Stimulus stimulus = new Stimulus();
Stopwatch watch = new Stopwatch();
stimulus.Show();
stimulus.Invoke((MethodInvoker)delegate { stimulus.perbaharuiStimulus("+"); });
watch.Start();
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Restart();
stimulus.Invoke((MethodInvoker)delegate { stimulus.perbaharuiStimulus("MAJU"); });
do
{
} while (watch.Elapsed.Seconds != 6);
watch.Restart();
stimulus.Invoke((MethodInvoker)delegate { stimulus.perbaharuiStimulus(""); });
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Stop();
stimulus.Close();
}
and heres code in formB
public Stimulus()
{
InitializeComponent();
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
public void perbaharuiStimulus(string stimulus)
{
this.Invoke((MethodInvoker)delegate
{
lbStimulus.Text = stimulus;
});
}
thankyou for attention..
You can change your code like below and it'll work fine.
Change perbaharuiStimulus code to
lbStimulus.Text = stimulus;
Change WorkerReportsProgress to True
Change backgroundWorker1_DoWork to below
Stimulus stimulus = new Stimulus();
Stopwatch watch = new Stopwatch();
backgroundWorker1.ReportProgress(1, stimulus);
watch.Start();
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Restart();
backgroundWorker1.ReportProgress(2, stimulus);
do
{
} while (watch.Elapsed.Seconds != 6);
watch.Restart();
backgroundWorker1.ReportProgress(3, stimulus);
do
{
} while (watch.Elapsed.Seconds != 2);
watch.Stop();
stimulus.Invoke((MethodInvoker)delegate { stimulus.Close(); });
Add the backgroundWorker1_ProgressChanged event and put below code in it
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Stimulus stimulus = ( Stimulus)e.UserState;
if(e.ProgressPercentage==1)
stimulus.perbaharuiStimulus("+");
if (e.ProgressPercentage == 2)
stimulus.perbaharuiStimulus("MAJU");
if (e.ProgressPercentage == 3)
stimulus.perbaharuiStimulus("");
stimulus.Show();
}
I hope this help you!
You shouldn't be creating the form in the background thread. Doing so will assign the form to that thread instead of the UI thread, meaning the form is now on a different thread than the message pump.
What you can do to fix this is to invoke the instantiation and viewing of the form on the UI thread, then your following Invoke calls should work.
Stimulus stimulus;
this.Invoke((MethodInvoker)delegate
{
stimulus = new Stimulus();
stimulus.Show();
});
//No need to invoke this since perbaharuiStimulus() calls Invoke() as well.
stimulus.perbaharuiStimulus("+");
//The rest of your code...
Apart from that you're doing everything correctly.
You can update a label in any form from a background worker using Invoke(...) like you did it. (Assuming stimulus is a field).
It is enough to call Invoke once. Stimulus.Invoke executes the delegate on the control thread of the stimulus form. So you can decide, whee you dispatch the thread. I'd recommend to do this in perbarauiStimulus, since that would reduce the chance that someone forgets to dispatch the call.
But there is one potential issues with your code:
Don't use exact comparison for elapsed time. Prefer using '>='. Since you are dealing with seconds this will rarely be an actual problem, but it may result in an infinite loop.
If stimulus isn't a field you have to create an instance of Stimulus outside of the background worker, because if you create it inside the worker Method, the form will run its message loop on the background workers thread. This eliminates the use of a background worker since the operation runs now synchronously from sytimulus view.
I have been using system.threading for a while now and I trying to wrap my head around tasks. How do you make thread safe calls to a UI control (for example a text box) from another thread using the TPL?
Here is a simple example where I want to update a text box everyone 1 second with the count of my secondary thread.
I have tried a few different methods but I can't seem to get it to work.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
await taskAsync();
}
private Task taskAsync()
{
return Task.Factory.StartNew(() => counter());
}
private void counter()
{
for (int i = 0; i < 10000; i++)
{
Task.Delay(1000).Wait();
textBox1.Text = i.ToString();
}
}
}
Is this even possible?
Thank you
Well in your current scenario I Think that a Progress would be most suitable.
I've made some alterations to your code below:
public partial class Form1 : Form
{
Progress<int> counterProgress;
public Form1()
{
InitializeComponent();
counterProgress = new Progress<int>();
counterProgress.ProgressChanged += CounterProgressUpdated;
}
private void CounterProgressUpdated(object sender, int e)
{
textBox1.Text = e.ToString();
}
private async void button1_Click(object sender, EventArgs e)
{
await taskAsync(counterProgress);
}
private Task taskAsync(IProgress<int> progress)
{
return Task.Factory.StartNew(() => counter(progress));
}
private async Task counter(IProgress<int> progress)
{
for (int i = 0; i < 10000; i++)
{
await Task.Delay(1000);
progress.Report(i);
}
}
}
Since the progress captures the current synchronization context on construction then you should be good to go as long as you create it on the UI thread.
Any handler provided to the constructor or event handlers registered with the ProgressChanged event are invoked through a SynchronizationContext instance captured when the instance is constructed. If there is no current SynchronizationContext at the time of construction, the callbacks will be invoked on the ThreadPool.
What about something like this?
private void counter() {
for (int i = 0; i < 10000; i++) {
Thread.Sleep(1000);
// “There's never an advantage in replacing Thread.Sleep(1000); in Task.Delay(1000).Wait();”
// http://stackoverflow.com/a/29357131/4267982
Dispatcher.Invoke(() => {
textBox1.Text = i.ToString();
});
}
}
Or, as an option, another way without separate thread at all (so there's no need in synchronization):
private async Task taskAsync() {
for (int i = 0; i < 10000; i++) {
await Task.Delay(1000);
textBox1.Text = i.ToString();
}
}
How do you make thread safe calls to a UI control (for example a text box) from
another thread using the TPL?
You generall do NOT.
If you need to update the UI, then you do that using Invoke. But from TPL directly - you do not want to update the UI too often because this always is a heavy redraw and unless you make a first person shooter frame rate does not matter THAT Much. 10 updates per second are PLENTY.
In this case you may want the tasks to update a central counter (using Interlocked classes) and once that goes above a certain threshhold in changes (or using a timer running in parallel) push that into the UI.
I have a Main form, which is running a synchronous operation(thus freezing the form).
Before that starts to happen I call my function showWaitWindow().
private void showWaitWindow()
{
Wait x = new Wait();
x.Show(this); //"this" is allowing the form to later centralize itself to the parent
}
This is where it is exactly happening:
if (result)
{
System.Threading.Thread t = new System.Threading.Thread(new
System.Threading.ThreadStart(showWaitWindow));
t.Start();
}
else
{
return;
}
propertyGrid1.SelectedObject = z.bg_getAllPlugins(); //Heavy synchronous call
//This should be closing the form, which is not happening.
for (int index = Application.OpenForms.Count; index >= 0; index--)
{
if (Application.OpenForms[index].Name == "Wait")
{
MessageBox.Show("found");
Application.OpenForms[index].Close();
}
}
I've tried this without threading as well, which didn't work as well. Also, because it's trying to centralize to the parent, while being created in another thread, it throws an exception "tried to access in different thread that it was created in" rephrasing.
How do I approach that?
I would suggest using a BackgroundWorker -- available in the WinForms toolbox.
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker1;
public Form1()
{
InitializeComponent();
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//perform lengthy operation in here.
}
}
Playing round with Timers.
Context: a winforms with two labels.
I would like to see how System.Timers.Timer works so I've not used the Forms timer.
I understand that the form and myTimer will now be running in different threads.
Is there an easy way to represent the elapsed time on lblValue in the following form?
I've looked here on MSDN but is there an easier way !
Here's the winforms code:
using System.Timers;
namespace Ariport_Parking
{
public partial class AirportParking : Form
{
//instance variables of the form
System.Timers.Timer myTimer;
int ElapsedCounter = 0;
int MaxTime = 5000;
int elapsedTime = 0;
static int tickLength = 100;
public AirportParking()
{
InitializeComponent();
keepingTime();
lblValue.Text = "hello";
}
//method for keeping time
public void keepingTime() {
myTimer = new System.Timers.Timer(tickLength);
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
void myTimer_Elapsed(Object myObject,EventArgs myEventArgs){
myTimer.Stop();
ElapsedCounter += 1;
elapsedTime += tickLength;
if (elapsedTime < MaxTime)
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (ElapsedCounter % 2 == 0)
this.lblValue.Text = "hello world";
else
this.lblValue.Text = "hello";
myTimer.Start();
}
else
{ myTimer.Start(); }
}
}
}
I guess your code is just a test so I won't discuss about what you do with your timer. The problem here is how to do something with an user interface control inside your timer callback.
Most of Control's methods and properties can be accessed only from the UI thread (in reality they can be accessed only from the thread where you created them but this is another story). This is because each thread has to have its own message loop (GetMessage() filters out messages by thread) then to do something with a Control you have to dispatch a message from your thread to the main thread. In .NET it is easy because every Control inherits a couple of methods for this purpose: Invoke/BeginInvoke/EndInvoke. To know if executing thread must call those methods you have the property InvokeRequired. Just change your code with this to make it works:
if (elapsedTime < MaxTime)
{
this.BeginInvoke(new MethodInvoker(delegate
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (ElapsedCounter % 2 == 0)
this.lblValue.Text = "hello world";
else
this.lblValue.Text = "hello";
}));
}
Please check MSDN for the list of methods you can call from any thread, just as reference you can always call Invalidate, BeginInvoke, EndInvoke, Invoke methods and to read InvokeRequired property. In general this is a common usage pattern (assuming this is an object derived from Control):
void DoStuff() {
// Has been called from a "wrong" thread?
if (InvokeRequired) {
// Dispatch to correct thread, use BeginInvoke if you don't need
// caller thread until operation completes
Invoke(new MethodInvoker(DoStuff));
} else {
// Do things
}
}
Note that current thread will block until UI thread completed method execution. This may be an issue if thread's timing is important (do not forget that UI thread may be busy or hung for a little). If you don't need method's return value you may simply replace Invoke with BeginInvoke, for WinForms you don't even need subsequent call to EndInvoke:
void DoStuff() {
if (InvokeRequired) {
BeginInvoke(new MethodInvoker(DoStuff));
} else {
// Do things
}
}
If you need return value then you have to deal with usual IAsyncResult interface.
How it works?
A GUI Windows application is based on the window procedure with its message loops. If you write an application in plain C you have something like this:
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
With these few lines of code your application wait for a message and then delivers the message to the window procedure. The window procedure is a big switch/case statement where you check the messages (WM_) you know and you process them somehow (you paint the window for WM_PAINT, you quit your application for WM_QUIT and so on).
Now imagine you have a working thread, how can you call your main thread? Simplest way is using this underlying structure to do the trick. I oversimplify the task but these are the steps:
Create a (thread-safe) queue of functions to invoke (some examples here on SO).
Post a custom message to the window procedure. If you make this queue a priority queue then you can even decide priority for these calls (for example a progress notification from a working thread may have a lower priority than an alarm notification).
In the window procedure (inside your switch/case statement) you understand that message then you can peek the function to call from the queue and to invoke it.
Both WPF and WinForms use this method to deliver (dispatch) a message from a thread to the UI thread. Take a look to this article on MSDN for more details about multiple threads and user interface, WinForms hides a lot of these details and you do not have to take care of them but you may take a look to understand how it works under the hood.
Personally when I work in an application that works with threads out of the UI one, I usually write this little snippet:
private void InvokeUI(Action a)
{
this.BeginInvoke(new MethodInvoker(a));
}
When I do an async call in a different thread I can always callback using:
InvokeUI(() => {
Label1.Text = "Super Cool";
});
Simple and clean.
As asked, here is my answer that checks for cross thread calls, synchronises variable updates, doesen't stop and start the timer and doesn't use the timer for counting elapsed time.
EDIT fixed BeginInvoke call. I've done the cross thread invoke using a generic Action, This allows the sender and eventargs to be passed. If these are unused (as they are here) it is more efficient to use MethodInvoker but I suspect the handling would need to be moved into a parameterless method.
public partial class AirportParking : Form
{
private Timer myTimer = new Timer(100);
private int elapsedCounter = 0;
private readonly DateTime startTime = DateTime.Now;
private const string EvenText = "hello";
private const string OddText = "hello world";
public AirportParking()
{
lblValue.Text = EvenText;
myTimer.Elapsed += MyTimerElapsed;
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
private void MyTimerElapsed(object sender,EventArgs myEventArgs)
{
If (lblValue.InvokeRequired)
{
var self = new Action<object, EventArgs>(MyTimerElapsed);
this.BeginInvoke(self, new [] {sender, myEventArgs});
return;
}
lock (this)
{
lblElapsedTime.Text = DateTime.Now.SubTract(startTime).ToString();
elapesedCounter++;
if(elapsedCounter % 2 == 0)
{
lblValue.Text = EvenText;
}
else
{
lblValue.Text = OddText;
}
}
}
}
First, in Windows Forms (and most frameworks), a control can only be accessed (unless documented as "thread safe") by the UI thread.
So this.lblElapsedTime.Text = ... in your callback is plain wrong. Take a look at Control.BeginInvoke.
Second, You should use System.DateTime and System.TimeSpan for your time computations.
Untested:
DateTime startTime = DateTime.Now;
void myTimer_Elapsed(...) {
TimeSpan elapsed = DateTime.Now - startTime;
this.lblElapsedTime.BeginInvoke(delegate() {
this.lblElapsedTime.Text = elapsed.ToString();
});
}
Ended up using the following. It's a combination of the suggestions given:
using System.Timers;
namespace Ariport_Parking
{
public partial class AirportParking : Form
{
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//instance variables of the form
System.Timers.Timer myTimer;
private const string EvenText = "hello";
private const string OddText = "hello world";
static int tickLength = 100;
static int elapsedCounter;
private int MaxTime = 5000;
private TimeSpan elapsedTime;
private readonly DateTime startTime = DateTime.Now;
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
public AirportParking()
{
InitializeComponent();
lblValue.Text = EvenText;
keepingTime();
}
//method for keeping time
public void keepingTime() {
using (System.Timers.Timer myTimer = new System.Timers.Timer(tickLength))
{
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.AutoReset = true;
myTimer.Enabled = true;
myTimer.Start();
}
}
private void myTimer_Elapsed(Object myObject,EventArgs myEventArgs){
elapsedCounter++;
elapsedTime = DateTime.Now.Subtract(startTime);
if (elapsedTime.TotalMilliseconds < MaxTime)
{
this.BeginInvoke(new MethodInvoker(delegate
{
this.lblElapsedTime.Text = elapsedTime.ToString();
if (elapsedCounter % 2 == 0)
this.lblValue.Text = EvenText;
else
this.lblValue.Text = OddText;
}));
}
else {myTimer.Stop();}
}
}
}
I have question about progressbar show value.
I have this main thread
private void button1_Click(object sender, EventArgs e)
{
progress prog = new progress();
progress.progressEvent += new progress.progressEventHandler(progressEvent);
for(int i=0;i<100;i++)
{
Thread.Sleep(100);
prog.incA();
}
}
void progressEvent(object sender)
{
if (progressBar1.InvokeRequired)
{
//Tady mi to caka az kym nedobehne cyklus for a pak zacne tohleto fungovat
progressBar1.Invoke(new ChangeProgressBarValue(ProgressStep));
}
else
{
ProgressStep();
}
}
public void ProgressStep()
{
progressBar1.PerformStep();
}
public class progress
{
private ThreadStart ts;
private Thread th;
private bool status = true;
public delegate void progressEventHandler(object sender);
public static event progressEventHandler progressEvent;
private int b,a = 0;
public progress()
{
ts=new ThreadStart(go);
th = new Thread(ts);
th.IsBackground = true;
th.Start();
}
public void incA()
{
a++;
if(a==100)
status = false;
}
private void go()
{
while (status)
{
if (a != b)
{
b = a;
if (progressEvent != null)
progressEvent(this);
}
}
th.Abort();
}
}
and my problem is IF start main thread and call IncA this method call event and in event is progressbar invoke
and this invoke waiting to end main thread FOR
why waiting?
thx
Your loop in the main thread is preventing "paint" events from happening. Since you are calling your progress bar's function from withing that thread, you will never see the updates.
You need to move the code to do the incrementing to another thread entirely.
Here is a sample of what you want to do using a Button, a BackgroundWorker, and a ProgressBar:
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Hope this helps!
The progress bar control is a UI object, and is created on the UI thread. When you call Invoke or BeginInvoke to update it, you are asking the UI thread to do the updating.
However, the UI thread is busy - in your button CLick event handler, you have a loop which Sleep()s the thread and calls prog.IncA in a loop. So it never exits back to the main UI loop (which is what dispatches windows messages and updates the UI). Your progress bar is being updated internally, but it never gets a chance to redraw because the UI thread is "busy".
The "processing" code (that is looping and calling prog.IncA()) should not be running on the UI thread at all - you need to start it off in a separate thread and then exit your Click handler so that the UI can continue to update.
Note that this has a side effect - if your UI thread is running, then the user will be able to continue interacting with your program, and so they can click again on the button and kick off another background thread - so you have to be very careful to make sure that the user can't do anything "dangerous" in the UI while you are busy processing.
I suggest you look at some introduction-to-threading tutorials to get an idea of how to use BackgroundWorker or another mechanism for running code in a separate thread. Once you understand that, you can add a progress bar. (And note that although a progress bar sounds like the simplest thing to do, it is actually rather a difficult thing to do due to the need for the UI thread to continue running but not let the user do anything dangerous during your processing)