Wednesday, March 18, 2009

Updating User Interface during long running process in .NET

When an application has long running process, you may need to notify the user interface by updating a text box or a progress bar, etc. I created the following application to demonstrate. This is a simple demo. When you click on "Test" button. The text box will be updated with progress information.
image
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000); // do something
this.textBox1.Text += @"
line "
+ Convert.ToString(i);
}
}



The code above will update the text box, but only after the whole loop, i.e 5 seconds. That was because this is a single thread application. The update to the text box will be executed after the click event.





If you want real time update, you will need to create a thread.









private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(DoSomeWork);
thread.Start();
}

private void DoSomeWork()
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000); // do something
this.textBox1.Text += @"
line "
+ Convert.ToString(i);
}
}



But when you run the code above, you will get "Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on." exception





image





That was because the text box was created by the main thread. You can not access it directly from a new thread. There are a lot of articles talking about this issue. You may check this one. Basically what they are saying is you need a way to call back from the new thread. Here is quote talking about the Control.InvokeRequired property from MSDN:







Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control.



Below is the sample code. When you run it, you will see that the text box will be updated every second.









public partial class Form1 : Form
{
private delegate void UICallerDelegate(int i);

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(DoSomeWork);
thread.Start();
}

private void DoSomeWork()
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000); // do something
UpdateTextbox(i);
}
}

private void UpdateTextbox(int i)
{
if (this.InvokeRequired)
{
UICallerDelegate dlg = new UICallerDelegate(UpdateTextbox);
BeginInvoke(dlg, i);
}
else
{
this.textBox1.Text += @"
"
+ Convert.ToString(i);
}
}
}


No comments:

Post a Comment