This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 7 years ago.
I simply want two progress bar to increment simultaneously on a button click; i have created two progress bars and one button start with two threads t1 and t2 each of which contains different method in order to increment the values of two progress bars (t1 for progressbar1 and t2 for progressbar2 respectively).
Controls I have:
Progress Bars: progressbar1, progressbar2
and Button button1.
The code is as follows:
Thread t1, t2;
private void Progressor1()
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
for (int i = 0; i <= 100; i++)
progressBar1.Value = i;
}
private void Progressor2()
{
progressBar2.Minimum = 0;
progressBar2.Maximum = 100;
for (int j = 0; j <= 100; j++)
progressBar2.Value = j;
}
private void button1_Click(object sender, EventArgs e) // Start Button
{
t1.Start();
t2.Start();
}
private void MultiThreadedcs_Load(object sender, EventArgs e)// Form Load
{
t1 = new Thread(Progressor1);
t2 = new Thread(Progressor2);
}
At run time it shows me following error:
Error Name = InvalidOperationException
Error Message = Cross-thread operation not valid: Control 'progressBar2' accessed from a thread other than the thread it was created on.
Please help me out with this. I have less idea of threading concepts, i have gone through this solution with no progress in my problem:
c# threading access to other thread
In the methods you've written to start as new threads you're calling the progress bar directly, so you're trying to edit data from another (the original) thread. Instead of directly editing the progress bar's value, you should Invoke a method which does, like so:
for (int j = 0; j <= 100; j++)
this.progressBar1.Invoke((Action) () => this.progressBar1.Value = j, null);
Related
Hi,guys, I don't understand how to handle the UI thread, so I try to create many UI elements in a child thread, but,
How to modify the following code so that the window don't block:
public void CreateCtr(string name)
{
Button btn = new Button();
btn.Content = name;
btn.Width = 10;
btn.Height = 10;
content.Children.Add(btn);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int count = 100000;
Task.Factory.StartNew(()=> {
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(()=> {
CreateCtr(i.ToString());
}));
}
});
}
You need to give the UI thread some space to breathe. With the current code, the UI thread is busy processing all the create button tasks and cannot get to processing user input. Put a minimal pause between creating each individual button. A few milliseconds are enough:
private void Button_Click(object sender, RoutedEventArgs e)
{
int count = 100000;
Task.Factory.StartNew(() => {
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(() => {
CreateCtr(i.ToString());
}));
Thread.Sleep(5);
}
});
}
The longer the pause, the more responsive the form becomes.
You will note that the form becomes less responsive again after some time, because after a certain amount of components on a form, adding another component takes very long. This is where my original comment becomes relevant: There is no usecase for having that many components on a form, so the framework is not designed to handle it well.
What might work is not putting all buttons on the same parent container but creating a tree of components where each component has no more than say 100 children. But again: I don't see any relevant usecase.
Might work to disable the dispatcher when adding the controls.
int count = 100000;
Task.Factory.StartNew(() => {
using (var d = Dispatcher.DisableProcessing())
{
for (int i = 0; i < count; i++)
{
this.Dispatcher.BeginInvoke(new Action(()=> {
CreateCtr(i.ToString());
}));
}
}
});
I'm creating a program that uses a TextBox and a ProgressBar and each time the text changes in the TextBox or the ProgressBar performs a step the GUI freezes and I can't do anything until the operation is done.
I know I need to run them on a different thread but I searched the entire web and didn't find a method that works for me.
This is an example when I write a file and I need to get the progress of it:
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
this.progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
and this is an example when I change text:
this.richTextBox1.Text = "Installing patch : ";
Any help would be appreciated.
Use a BackgroundWorker, put your for loop inside of DoWork method:
backgroundWorker1.DoWork += doWork;
private void doWork(object sender, DoWorkEventArgs e)
{
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
this.progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
}
When you want to execute your loop call backgroundWorker1.RunWorkerAsync() method.
Also you can use ProgressChanged event to update your progress bar:
backgroundWorker1.DoWork += doWork;
backgroundWorker1.ProgressChanged += progressChanged;
private void doWork(object sender, DoWorkEventArgs e)
{
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
var value = int.Parse(Math.Truncate(percentage).ToString());
backgroundWorker1.ReportProgress(value);
}
}
private void progressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Update: If you want to pass arguments to DoWork method you can do it when you calling the backgroundWorker1.RunWorkerAsync() method like this:
backgroundWorker1.RunWorkerAsync(argument);
And you can access it in DoWork method with the e.Argument. if you want to pass more than one argument you can do the trick with Anonymous Types.For example:
var myArguments = new { Property1 = "some value", Property2 = myId, Property3 = myList };
backgroundWorker1.RunWorkerAsync(myArguments);
In DoWork method:
dynamic arguments = e.Argument;
var arg1 = arguments.Property1;
var arg2 = arguments.Property2;
// and so on...
You could use the Task class to create and run background threads. A word of caution! Any time you want to update something from a thread that is not the main thread, you must use the Dispatcher to run code on the main thread.
My example code:
System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(() =>
{
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
//This runs the code inside it on the main thread (required for any GUI actions
App.Current.Dispatcher.Invoke((Action) (() =>
{
this.progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
}
});
task.Start();
I wrote a short method that uses a for loop 2 times:
progressBar1.Minimum = 1;
progressBar1.Maximum = 1000000;
progressBar1.Step = 1;
for (int idx = 1; idx < 1000000; idx++)
{
progressBar1.PerformStep();
}
Thread.Sleep(2000);
progressBar1.Invalidate();
this.Update();
progressBar1.Value = 1;
for (int idx = 1; idx < 1000000; idx++)
{
progressBar1.PerformStep();
}
My question is this: on the 1st pass, when the value gets to 1000000 the actual colored bar only
displays about 50-75% across, but never 100% across, even though the value has reached the maximum.
This is unique to using the progressbar multiple times in one method.
How can I get the actual colored bar to get to the very end?
(Apparently its a buffering thing?)
The problem is that you're blocking your UI thread. Don't do that. Perform all the long-running tasks in a background thread (e.g. a BackgroundWorker) and marshal back to the UI thread to update the progress bar (BackgroundWorker makes this pretty trivial)... then all should be well.
As Jon said, doing work on your UI thread interferes with IU updates, making the interface unresponsive. That's why you do the work on a background thread.
Use a BackgroundWorker and update your progress bar on the worker's ProgressChanged event:
{
progressBar1.Value = 1;
progressBar1.Minimum = 1;
progressBar1.Maximum = 100;
progressBar1.Step = 1;
var bgw = new BackgroundWorker();
bgw.ProgressChanged += bgw_ProgressChanged;
bgw.DoWork += bgw_DoWork;
bgw.WorkerReportsProgress = true;
bgw.RunWorkerAsync();
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
// do your long running operation here
for (int idx = 1; idx <= 100; idx++)
// when using PerformStep() the percentProgress arg is redundant
((BackgroundWorker)sender).ReportProgress(0);
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.PerformStep();
}
progressBar.Invoke((MethodInvoker)delegate ()
{
this.progressBar.Value = (int)((100 * indexOFfreq) / countFReq);
PB_persent.Text = progressBar.Value.ToString() + "%";
});
Application.DoEvents();
I'm having an application with a progress bar and a buttom.
When the button clicked the progress bar value will get increased, here is the source code,
private void Run()
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
private void button1_Click(object sender, EventArgs e)
{
Run();
}
so when i run the application, the progress bar value is getting increased, but when i try to move the window its not responding.
I can not run it in a normay thread way - it will throw Cross-Thread error.
so i changed the code like,
private void Run()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(this.Run));
}
else
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(new ThreadStart( Run));
myThread.Start();
}
Now i can able to move the winodow, but when i move the progress bar is stopped, and when i release the mouse button its resuming. So still the execution is in UI Thread.
How to handle it in a better way.Please help me to do this .
Invoke() works by running the given delegate from the UI thread. So if you use Invoke() to run your entire method, then your entire method runs from the UI thread.
Instead, you should be doing your actual work in the other thread, and just performing UI updates in the UI thread, by just Invoke()ing the little bits of code that perform the updates.
One easy way to do this is to use the BackgroundWorker class built into the standard library.
This has been answered here - in your case the code should look something like:
this.BeginInvoke((Action)(() => progressBar1.Value++));
I am using WPF trying to run a Thread in the background that updates a progress bar. I don't wan to block the UI thread so I am running the following code. However the UI still blocked. It seems so simple, what am I doing wrong?
Dispatcher.BeginInvoke(
(ThreadStart) delegate(){
for(double i = progressBar_ChangeProgress.Minimum;
i < progressBar_ChangeProgress.Maximum;
i++)
{
for (int b = 0; b < 100000000; b++) { }
progressBar_ChangeProgress.Value = i;
}
EnableAllInputControls();
}, DispatcherPriority.Background);
Why not leverage the BackgroundWorker in this scenario...
void Go()
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar_ChangeProgress.Value = e.ProgressPercentage;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int b = 0; b < 100; b++)
{
Thread.Sleep(100);
worker.ReportProgress(b);
}
}
UPDATE:
If you are wanting to use the Dispatcher; set the priority to Normal and perform the processing on the background thread then calling a method on the UI thread to provide the update.
void Go()
{
ThreadStart start = delegate()
{
//this is taking place on the background thread
for (int i = 0; i < 100; i++)
{
//this is slowing things down; no real relevance
Thread.Sleep(100);
//this will marshal us back to the UI thread
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action<int>(Update), i
);
}
};
new Thread(start).Start();
}
void Update(int value)
{
//this is taking place on the UI thread
_progressBar.Value = value;
}
The Dispatcher is just a mechanism to run a bit of code on the UI thread at a later time. The priority you're passing in controls when it will get executed, not any type of thread priority. The contents of your delegate in this case are getting run on the UI thread. Using a BackgroundWorker as mentioned by Aaron would certainly help here.
Also I might point out that usually a progress bar shows how close a task is to completing. If you don't know how long something is going to take or have no way of measuring progress, you can use an Indeterminate progress bar. Only update the value if you have some meaningful information. (though you may have just provided this for demonstration purposes)
Everything inside BeginInvoke is being run on the UI thread and so it will block.
What you need to do is run any time intensive code in your thread and then just update the UI inside the Invoke.
You want something more like this:
for (double i = progressBar_ChangeProgress.Minimum;
i < progressBar_ChangeProgress.Maximum;
i++)
{
for (int b = 0; b < 100000000; b++) { }
Dispatcher.Invoke((ThreadStart) delegate(){
progressBar_ChangeProgress.Value = i;
});
}