C# Threading still make UI unresponsive - c#

Well , I have a form1 which has buttons and if you click one of its button
It would load the UserControl into panel in form1
That usercontrol1 contains a lot of data like Database,charts and picture boxes too. So it would definitely make the User Interface unresponsive while loading.
So I read some article and I found out that I need to run it through another thread so I tried it and it just increase the performance by a little bit.
The usercontrol1 still make the GUI unresponsive for about 3-5 sec and what if my data become larger.
I want to make it responsive and show to user that still loading by running the animated picturebox and stop if its finish loading
here is my code:
private void click_dashb_Click(object sender, EventArgs e)
{
ParameterizedThreadStart pts = new ParameterizedThreadStart(load_UserControl);
Thread t = new Thread(pts);
t.Start();
//Animated Picturebox to show user that UI is loading
pictureBox1.Enabled = true;
hover.Location = new Point(42, 130);
}
private void load_UserControl(object state)
{
Invoke(new Action(() =>
{
//load user control through another thread
while (panel1.Controls.Count > 0)
panel1.Controls[0].Dispose();
Home frm = new Home();
frm.AutoScroll = true;
panel1.Controls.Add(frm);
frm.Show();
}));
//Stop the animated GIF means the load is finish!
pictureBox1.Enabled = false;
}
If you help me about this problem. I might apply it to all of my works. because most of it contains large data.
Thanks stackoverflow community :)
EDIT:
After reading the comments suggesting to use Background worker . I tried to use it. but still getting a little bit unresponsiveness
Here's the new code:
private void click_dashb_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
try
{
BeginInvoke((MethodInvoker)delegate
{
while (panel1.Controls.Count > 0)
panel1.Controls[0].Dispose();
Home frm = new Home();
frm.AutoScroll = true;
panel1.Controls.Add(frm);
frm.Show();
});
}
catch (Exception x)
{
MessageBox.Show("An error occured while performing operation" + x);
}
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Operation Cancelled");
}
else
{
MessageBox.Show("Operation Completed");
}
}
It's a little bit better but i still got a little unresponsiveness. Can you check out my code and what's wrong with this.?
thanks again

The problem with your code is that although you run the load_UserControl code in a new thread, that code calls Invoke which effectively makes al the code run on the UI thread again. I can image you did that because accessing Forms and PictureBoxes requires running on the UI thread.
The solution (in general) is to do non-UI work on a seperate thread and then switch back to the UI thread to update the visual controls.
To do this, you can conveniently use the BackgroundWorker class. In the DoWork event handler you do the heavy computation, in the RunWorkerCompleted event handler you update the controls. If you want, you can even update some controls (like a progressbar) during the work by means of the ProgressChanged event handler.

Well, just starting a new thread doesn't make the UI responsive by definition. You need to make the thread so that it actually does stuff in parallel.
Your thread does not, as it basically executes all code in this.Invoke.
That being said: Your code needs to be executed in this.Invoke, as almost everything you do needs to be done on the UI thread.
So in your case, there's really no point in parallelizing stuff, as there's no way to do what you want to do without blocking the UI thread and no technique I know of (Threads, Tasks, BackgroundWorker, etc.) will solve this problem.

Related

thread call fires inconsistently

I am a beginner in high level programming languages. I am trying to make an WForms app for a serial port , im using VS 2010 C#
I get the following error:
Cross-thread operation not valid: Control 'rtxtDataArea' accessed from a thread other than the thread it was created on.
This happens here:
private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}
I have tried to change the color of a textbox when I receive some data.
It fires that cross thread error.
The question is why it does not fire the same error here, when I try to change the color of a label?
private void btnConnect_Click(object sender, EventArgs e)
{
if (ComPort.IsOpen)
{
disconnect();
}
else
{
connect();
rdText.ForeColor = Color.Blue;//ok, it works
}
}
; this works ; the first does not.
Why? Is not the ComPort_DataReceived_1 the same nature as btnConnect_Click ?
Or what is the reason?
I have read a lot about threads, but I understood nothing I can use, Can someone give an intuitive explanation ?
In winforms there is only one thread that may change anything on the UI like enable buttons, change text boxes, etc. Usually this is the UI thread. Quite often this is the only thread you have.
However, if you start a new thread, this thread might want to change the UI. This happens especially if this new thread fires an event that is received by your form.
Whenever you see the message accessed from a thread other than the thread it was created on, you can be almost certain this is the case.
The most easy solution to solve this is using the functions Control.IsInvokeRequired and Control.Invoke. The pattern to do this is as follows. The following function updates myButton on myForm
private void UpdateMyButton (MyType myParameter)
{
if (myButton.InvokeRequired)
{ // this is not the thread that created the button to update
this.Invoke(new MethodInvoker( () => this.UpdateMyButton(myParameter)));
// this will let the UI thread call this function with the same parameter.
}
else
{ // Now called by the UI thread: this thread may change myButton
myButton.Enabled = myParameter.ShouldButtonEnable;
myButton.Text = myParameter.ButtonText;
}
}
By the way, if you have to update several controls on your form you ought to check InvokeRequired for each of these controls. However, since they are usually created by the same UI thread it is sufficient to check for this.InvokeRequired.
Control.Invoke returns after the invoke is completed, so after all items are updated. Upon return of Invoke you can use the result of UpdateMyButton.
If you don't want your non-ui thread to wait for completion of UpdateMyButton, consider the use of Control.BeginInvoke: "hey UI thread, whenever you've got time, can you UpdateMyButton for me. Of course in that case you can't use the results of UpdateMyButton
Because "DataReceived" runs on another thread and not UI thread. You must use Invoke for that :
private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.
if (InvokeRequired)
{
// If not on UI thread, then invoke
Invoke(new MethodInvoker(() =>
{
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}));
}
else
{
// On UI thread, invoke not needed
// Show in the terminal window
rtxtDataArea.ForeColor = Color.Green; // error ,
rtxtDataArea.AppendText(recievedData + "\n");
}
}

C# Threading and upgrading GUI

I've this situation:
Form "Menu" where you can click "New" and an insert form (with showdialog) will appear. If you insert data, I make an INSERT query on the database and I close this form.
After showDialog() statement, I have a lot of methods who performs a lot of operations in a sequential way (MUCH IMPORTANT) and sometimes update a dataGridView.
After all this computation, I simulate press of "new button" in order to allow user to insert a new item.
This is an example:
private void buttonNew_Click(object sender, EventArgs e)
{
DialogResult dr = new DialogResult();
InsertForm form = new InsertForm();
dr = form.ShowDialog();
if (dr == System.Windows.Forms.DialogResult.OK)
{
Method1();
Method2();
if (dataGrid2.RowCount > 0)
{
Method3();
Method4();
Method5();
Method6();
Method7();
Method8();
}
bNew.PerformClick();
}
}
The problem is that the "New" form (bNew.PerfomClick()) appear after a 2-3 seconds and I can't wait so much time. So I tried to create a method who include Method1 to Method8, run it in a new Thread and execut bNew.PerfomClick(), but this doesn't works because a lot of my methods update a datagridView.
Is there a way to solve this problems?
Sorry for my bad english.
------------UPDATE------------------
Now I'm trying this code:
delegate string OperazioniDelegate();
private string Operazioni()
{
if (!InvokeRequired)
{
Method1();
Method2();
............
}
else
Invoke(new OperazioniDelegate(Operazioni));
return "";
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Operazioni();
}
In this way, new insert form is showed instantly but his UI is blocked until the backgroundWorker End work..
Assuming you are running your long running operation in a seperate thread, which updates the UI elements (datagridview in your case), it is bound to experience cross thread exception, because the context in which background thread runs is different from that of UI thread. Either use InvokeRequired as given below:
delegate void valueDelegate(string value);
private void SetValue(string value)
{
if (someControl.InvokeRequired)
{
someControl.Invoke(new valueDelegate(SetValue),value);
}
else
{
someControl.Text = value;
}
}
or use BackGroundWorker class, which will do automatic marshalling of calls from background thread to Ui thread. Use of Background worker is given in this link http://www.albahari.com/threading/part3.aspx#_BackgroundWorker

Main thread locking up in C#

I am faced with a problem. I am clicking a button that is calling several methods, although the main thread is locking up, so I created an instance of my class (which is Form1) e.g. Form1Object and then the button called the methods as so: Form1Object.Check1 and so on.
Although the thread still locked up (i.e. the GUI became unresponsive for a period) Is there anyway of getting around this, any examples would be greatly appreciated.
The code in question is below:
private void StartChecks_Click(object sender, EventArgs e)
{
Form1 Form1Object = new Form1();
Form1Object.InitChecks();
}
public void InitChecks()
{
Check1();
Check2();
Check3();
Check4();
Check5();
Check6();
Check7();
}
Creating a new Form does not start a new Thread.
You will have to move those CheckN() methods to a BackgroundWorker.
private void StartChecks_Click(object sender, EventArgs e)
{
Form1 Form1Object = new Form1();
var worker = new BackgroundWorker();
worker.DoWork += (s, arg) =>
{
Form1Object.InitChecks();
};
// add progress, completed events
worker.RunWorkerAsync();
}
But note that this require that the checks are independent and do not interact with any Control.
What you need to do is start a parallel thread to do the check, so you won't lock up the main thread:
private void StartChecks_Click(object sender, EventArgs e)
{
Form1 Form1Object = new Form1();
Thread t = new Thread(
o =>
{
Form1Object.InitChecks();
});
t.Start();
}
Hopefully you don't need to actually retrieve anything from those calculations, so you can just fire and forget about it.
You have several options here, and use them depending of your skill/preference/requirement:
if you don't update anything on the form while you process, start another thread and call everything on that thread, and update UI when appropriate (when everything is finished)
if you need to update things on your form while processing, you have several options:
either use Application.DoEvents() from the processing loop of every method you use
start a new thread then update form controls with Invoke() - if you try to update them directly, you'll be in trouble
If you care to comment and decide for one of the options, I can provide more info on just that...

How can a new Form be run on a different thread in C#?

I'm just trying to run a new thread each time a button click even occurs which should create a new form. I tried this in the button click event in the MainForm:
private void button1_Click(object sender, EventArgs e)
{
worker1 = new Thread(new ThreadStart(thread1));
worker2 = new Thread(new ThreadStart(thread2));
worker1.Start();
worker2.Start();
}
private void thread1()
{
SubForm s = new SubForm();
s.Show();
}
private void thread2()
{
SubForm s = new SubForm();
s.Show();
}
The code in the Subform button click event goes like this:
private void button1_Click(object sender, EventArgs e)
{
int max;
try
{
max = Convert.ToInt32(textBox1.Text);
}
catch
{
MessageBox.Show("Enter numbers", "ERROR");
return;
}
progressBar1.Maximum = max;
for ( long i = 0; i < max; i++)
{
progressBar1.Value = Convert.ToInt32(i);
}
}
Is this the right way? Because I'm trying to open two independent forms, operations in one thread should not affect the other thread.
Or is BackGroundworker the solution to implement this? If yes, can anyone please help me with that?
You do not need to run forms in separate threads. You can just call s.Show() on multiple forms normally. They will not block each other.
Of course, if you’re doing something else, like some sort of calculation or other task that takes a long while, then you should run that in a separate thread, but not the form.
Here is a bit of code that will let you create a progress bar that shows progress for a long process. Notice that every time to access the form from inside the thread, you have to use .Invoke(), which actually schedules that invocation to run on the GUI thread when it’s ready.
public void StartLongProcess()
{
// Create and show the form with the progress bar
var progressForm = new Subform();
progressForm.Show();
bool interrupt = false;
// Run the calculation in a separate thread
var thread = new Thread(() =>
{
// Do some calculation, presumably in some sort of loop...
while ( ... )
{
// Every time you want to update the progress bar:
progressForm.Invoke(new Action(
() => { progressForm.ProgressBar.Value = ...; }));
// If you’re ready to cancel the calculation:
if (interrupt)
break;
}
// The calculation is finished — close the progress form
progressForm.Invoke(new Action(() => { progressForm.Close(); }));
});
thread.Start();
// Allow the user to cancel the calculation with a Cancel button
progressForm.CancelButton.Click += (s, e) => { interrupt = true; };
}
Although I'm not 100% aware of anything that says running completely seperate forms doing completely isolated operations in their own threads is dangerous in any way, running all UI operations on a single thread is generally regarded as good practice.
You can support this simply by having your Subform class use BackgroundWorker. When the form is shown, kick off the BackgroundWorker so that it processes whatever you need it to.
Then you can simply create new instances of your Subform on your GUI thread and show them. The form will show and start its operation on another thread.
This way the UI will be running on the GUI thread, but the operations the forms are running will be running on ThreadPool threads.
Update
Here's an example of what your background worker handlers might look like - note that (as usual) this is just off the top of my head, but I think you can get your head around the basic principles.
Add a BackgroundWorker to your form named worker. Hook it up to the following event handlers:
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Executed on GUI thread.
if (e.Error != null)
{
// Background thread errored - report it in a messagebox.
MessageBox.Show(e.Error.ToString());
return;
}
// Worker succeeded.
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Executed on GUI thread.
progressBar1.Value = e.ProgressPercentage;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// Executed on ThreadPool thread.
int max = (int)e.Argument;
for (long i = 0; i < max; i++)
{
worker.ReportProgress(Convert.ToInt32(i));
}
}
Your click handler would look something like:
void button1_Click(object sender, EventArgs e)
{
int max;
try
{
// This is what you have in your click handler,
// Int32.TryParse is a much better alternative.
max = Convert.ToInt32(textBox1.Text);
}
catch
{
MessageBox.Show("Enter numbers", "ERROR");
return;
}
progressBar1.Maximum = max;
worker.RunWorkerAsync(max);
}
I hope that helps.
Try this. It runs the new Form on its own thread with its own message queues and what not.
Run this code:
new Thread(new ThreadStart(delegate
{
Application.Run(new Form());
})).Start();
Use Thread.CurrentThread.GetHashCode() to test that is runs on different thread.
It's possible to run different forms on different threads. There are two caveats I'm aware of:
Neither form may be an MDI client of the other. Attempting to make a form an MDI client of another when the forms have different threads will fail.
If an object will be sending events to multiple forms and all forms use the same thread, it's possible to synchronize the events to the main thread before raising it. Otherwise, the event must be raised asynchronously and each form must perform its own synchronization mechanism for incoming events.
Obviously it's desirable not to have any window's UI thread get blocked, but using separate threads for separate windows may be a nice alternative.

WPF application in a loop, how to not have the whole application freeze?

I am having fun with WPF and got a problem. I have googled and found this website that has the same problem of me but without any working solution.
The problem is that I have a button that do some processing of data (around 30 sec). I want to have the button to disable and to have log writing in a text box... the problem is that it doesn't disable and it doesn't wrote any thing on the textbox until the processing is completely done.
Any idea?
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
//Long stuff here
txtLog.AppendText(Environment.NewLine + "Blabla");
//End long stuff here
this.button1.IsEnabled = true;
}
As others have said, use the BackgroundWorker or some other method of doing work asychronously.
You can declare it under your Window, initialize it somewhere like the Loaded event, and use it in the Click event. Here's your method, modified to use BackgroundWorker, assuming you've declared it under the Window as _bw:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_bw = new BackgroundWorker();
_bw.DoWork += new DoWorkEventHandler((o, args) =>
{
//Long stuff here
this.Dispatcher.Invoke((Action)(() => txtLog.AppendText(Environment.NewLine + "Blabla")));
});
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, args) =>
{
//End long stuff here
this.Dispatcher.Invoke((Action)(() => this.button1.IsEnabled = true));
});
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
_bw.RunWorkerAsync();
}
Note that anything that modifies your UI from another thread must be done within a Dispatcher.Invoke or Dispatcher.BeginInvoke call, WPF does not allow you to get or set DependencyProperty values from any thread but the one where the object was created (more about this here).
If you wanted to read from txtLog instead of modifying it, the code would be the same:
//Long stuff here
this.Dispatcher.Invoke((Action)(() =>
{
string myLogText = txtLog.Text;
myLogText = myLogText + Environment.NewLine + "Blabla";
txtLog.Text = myLogText;
}));
That operation is being performed on the UI thread. This means that it will block the Windows message pump from processing until it has completed. no pump = no UI updates. You should launch the job on another thread. I don't know WPF, but in C# I would use either the Thread or BackgroundWorker classes.
do it async. create a backgroundworker process to handle the data and the application will continue to respond. MSDN Resources on the Class. Since WPF is using C# (or VB.net) you can still use the same types of threading objects. I've used the background worker successfully in a WPF app myself.

Categories