c# .net Winforms,IDE: VS 2010
I having two windows from F1, F2.
F1 is the caller, and F2 is the form which I want to load in thread because it is having lots of rich controls on it.
I am able to load the f2 in child thread but it just get visible and goes, because its on child thread.(See Case 1 Code)
Case 1 Code
private void button1_Click(object sender, EventArgs e)
{
StartProgress();
Thread th = new Thread(new ThreadStart(LoadForm));
th.Start();
}
private void StartProgress()
{
progressBar1.Maximum = 100;
progressBar1.Step = 1;
for (int i = 0; i <= 100; i++)
{
label1.Text = i + "%";
progressBar1.PerformStep();
Thread.Sleep(10);
label1.Refresh();
}
}
private void LoadForm()
{
Form2 f2 = new Form2();
f2.Show();
}
}
Then I did reverse that is I loaded progress bar in child thread and loaded f2 on main thread.(See Case 2 Code)
//Code Case2:
case 2 when progress bar is on child thread.
private void button1_Click(object sender, EventArgs e)
{
LoadForm();
Thread th = new Thread(new ThreadStart(StartProgress));
th.Start();
LoadForm();
}
private void StartProgress()
{
progressBar1.Maximum = 100;
progressBar1.Step = 1;
for (int i = 0; i <= 100; i++)
{
label1.Text = i + "%";
progressBar1.PerformStep();
Thread.Sleep(10);
label1.Refresh();
}
}
private void LoadForm()
{
Form2 f2 = new Form2();
f2.Show();
}
but case 2 having 2 problems.
Problem 1: It loads the f2 as usual with flicking.
Problem 2: Cross Thread Opration progressbar1
//Pls suggest how to load the f2 in background and show it after the progress bar is loaded.
If you need to load a secondary window in the background, a better approach would be to load the data in the background of your current UI and then pass that information to the new Window.
public void btnNewWindow_Click(object sender, EventArgs args)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += WorkerOnDoWork;
worker.ProgressChanged += WorkerOnProgressChanged;
worker.RunWorkerCompleted += WorkerOnRunWorkerCompleted;
worker.RunWorkerAsync();
}
private List<string> _data = new List<string>();
private void WorkerOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
//Work has finished. Launch new UI from here.
Form2 f2 = new Form2(_data);
f2.Show();
}
private ProgressBar progressBar1;
void WorkerOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Log process here
}
private void WorkerOnDoWork(object sender, DoWorkEventArgs e)
{
//Perform work you need to load the data.
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(50);
_data.Add("Test" + i);
}
}
I put this together real quick in WPF but you should be able to make this work with the same principles. I will reiterate that you shouldn't use a second UI thread. Load the data up front and then pass along to the window when ready and available.
If all you want to do is avoid the flicker, create F2 as hidden, and only show it once its initialization is complete.
It's been a while since I did any Windows Forms programming, but I guess you can show the form in your _Load event handler.
You shouldn't have more than one User Interface (UI) thread. The complexity to keep the data between your interface will be quite difficult and often make the application Not Thread Safe. Which will cause error after error for you.
The primary goal should keep the User Interface (UI) thread responsive. This way as the user interacts with your application the interface is responsive as the application performs computational task.
Here is a great article on keeping your UI thread responsive here.
You may want to note what Microsoft says here:
Note: Not all changes to the UI are necessarily done on the UI thread.
There's a separate render thread that can apply UI changes that won't
affect how input is handled or the basic layout. For example many
animations and transitions that inform users that a UI action has
taken place can actually run on this render thread. But if it's your
code that is changing UI elements somewhere in your pages, it's best
to assume that your code could have the potential to block the UI
thread, unless you're familiar with those APIs or subsystems and know
for certain they don't affect the UI thread.
Update:
I didn't realize you were using Windows Forms, the article listed above is for XAML. You can use this article to help build a responsive UI with computational task here for Windows Forms. (Link is for WPF, you'll need to port for Windows Forms).
Related
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.
I'm a fairly new to .net and I'm still struggling to understand a lot things, and right now I'm trying to accomplish something relatively simple but I've failed every single time, I would like to add a Thread to my program, this Thread would be responsible to perform the Upload operations to a web server and keep my program responsive providing the feedback of the operations to my users by updating a ListView, where the users would see all the status of the file uploads.
I don't know how to put this Thread inside the program to make it responsive, I couldn't find any examples so far and I'm trying to find a little sample to show me the use of Thread and WinForms in action.
What you'll want to use is a BackgroundWorker. It's specifically designed for exactly this purpose.
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += (_, args) => LongRunningTask(bgw);
bgw.WorkerReportsProgress = true;
bgw.ProgressChanged += (_, args) =>
{
textbox1.Text = args.ProgressPercentage.ToString();
};
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
bgw.RunWorkerAsync();
}
private void LongRunningTask(BackgroundWorker bgw)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);//placeholder for real work
bgw.ReportProgress(i);
}
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do stuff when completed.
}
A key point to note is that the DoWork event runs in a background thread, but the other events all run in the UI thread. The BackgroundWorkder class takes care of ensuring that all on its own.
I have a method that has a for loop in it. In the for loop I want to update some label's text on the mainform, but the changes are only done after the loop ends.
I tried to do it on another thread like this:
Thread firstthread = new Thread(new ThreadStart(myMethod));
firstthread.Start();
When I did that I got an InvalidOperationException because of trying to access controls on another thread or something like that.
How should I update the labels(or other controls) on the mainform from a loop while the loop is in progress?
You should use a BackgroundWorker. Place your long running loop inside of the DoWork event handler; it will run in a background thread and not block the UI thread. You can set ReportProgress to true and then attach an event handler to that to allow you to update a label (or whatever else) periodically. The ProgressReported event runs in the UI thread. You can also add a handler to the Completed event which runs in the UI thread as well.
You can look at the MSDN page for BackgroundWorker for details and code samples.
You should check the Invoke and BeginInvoke methods on the Form (for Windows.Forms) or on the Dispatcher object of the window (for WPF).
For example:
this.BeginInvoke(new Action(() => this.Text = "ciao"));
changes the title bar of the form.
BeginInvoke is asynchronous - it doesn't wait for the change to happen - while Invoke is synchronous and blocks until the change is done. Unless you have specifically that need, I would suggest using BeginInvoke which reduces the chances of an accidental deadlock.
This will allow you to update UI from a concurrent thread - and works whatever threading mechanism you are using (TPL tasks, plain Thread, etc.).
As Servy said, you can use something like in this simple example:
public partial class Form1 : Form
{
BackgroundWorker bgw;
public Form1()
{
InitializeComponent();
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.WorkerReportsProgress = true;
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string text = (string)e.UserState;
SetValue(text);//or do whatever you want with the received data
}
void SetValue(string text)
{
this.label1.Text = text;
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10000; i++)
{
string text = "Value is " + i.ToString();
bgw.ReportProgress(1, text);
Thread.Sleep(1000);
}
}
private void button1_Click(object sender, EventArgs e)
{
bgw.RunWorkerAsync();
}
}
I have a form (Developed in C# using VS2010) with a Progress Bar.
It's kind of stopwatch form where I fill the progress bar in say 10secs.... As Time elapses, Progress bar fills accordingly.... Means after 5secs, Progress Bar will be filled 50% and so on....
I used a for loop to perform this operation:-
for(int count=0;count<200;count++)
{
progbar.performstep();
progbar.update();
update();
Thread.Sleep(50);
}
I have used Thread.Sleep of 50msec so that progress bar updates smoothly.
For 1sec, it increments in steps.
Problem is if I do anyother operation like Moving this form or even clicking on another icon on desktops makes this form "Not Responding".... But it perfoms the operation and at the end of 10 secs, It regains it's state and shows the pop up indicating 10secs are elapsed with Progress Bar Full.
Thanks for help and Sorry for using such complicated language.
Regards,
Swanand
Update: I solved this problem with the help of Answers below.... One common mistake I got to know is forgetting "Applications.DoEvents()" along with "update()".... If you enter this line, there are less chances of getting "hanged"!
You're performing a long-running operation on the UI thread, which means that the UI "message loop" (responsible for handling events such as user input and updating the screen) doesn't get a chance to run.
You should perform the long-running operation on a different thread (whether one you create yourself or a background thread) and either use BackgroundWorker to easily update your progress bar, or use Control.Invoke/BeginInvoke to marshall a delegate call back to the UI thread when you need to update the UI. (You mustn't update controls from the wrong thread.)
If your only UI interaction is filling in a progress bar, I suggest using BackgroundWorker.
If you're not really doing "real" work, just waiting for time to pass, you could use a System.Windows.Forms.Timer instead of all of this, however. That will "tick" on the UI thread, but won't block the UI thread between ticks. You should only use this if you don't have a lot of work to do though - if it really is just updating a progress bar, not (say) processing a file etc. Note that you shouldn't rely on the timer firing exactly "on time" - you should probably set the position of the progress bar based on the observed time, rather than the observed number of ticks.
You are blocking the UI thread, which means it isn't processing events such as "paint". To do this properly, you should be using something like BackgroundWorker, and just updating the UI from the progress event.
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyForm());
}
}
class MyForm : Form
{
Button btn;
BackgroundWorker worker;
ProgressBar bar;
public MyForm()
{
Controls.Add(btn = new Button { Text = "Click me" });
btn.Click += new EventHandler(btn_Click);
Controls.Add(bar = new ProgressBar { Dock = DockStyle.Bottom, Visible = false, Minimum = 0, Maximum = 100 });
worker = new BackgroundWorker { WorkerReportsProgress = true };
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bar.Visible = false;
if (e.Error != null)
{
Text = e.Error.Message;
}
else if (e.Cancelled)
{
Text = "cancelled";
}
else
{
Text = e.Result == null ? "complete" : e.Result.ToString();
}
btn.Enabled = true;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
for (int count = 0; count < 100; count++)
{
worker.ReportProgress(count);
Thread.Sleep(50);
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
bar.Value = e.ProgressPercentage;
}
void btn_Click(object sender, EventArgs e)
{
bar.Value = 0;
bar.Visible = true;
btn.Enabled = false;
worker.RunWorkerAsync();
}
}
You are blocking the Main UI thread. You can use a background worker to do this. You can find more details in MSDN
If you want to run your code you should put this code in a function and call this function with one thread.
public static void fun1()
{
for (int i = 0; i <= 10; i++)
{
Console.Write("This is function1");
Console.Write("\n");
}
}
Thread firstthread = new Thread(new ThreadStart(fun1));
firstthread.Start();
firstthread.suspend();//whenever you want your current control to stop.
b'caz Thread.sleep(100) will stop the whole context not that particular you want..
Answer suggested by Marc will help. Lon running operations can make your application crash or not responsive. I have a blog post related to the usage of the background worker class.
http://midnightprogrammer.net/post/Using-Background-Worker-in-C.aspx
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.