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;
});
}
Related
I'm trying to workout how to call chart1.Series[0].Points.AddXY(1, 4); from another thread.
I have been trying to adapt the examples which all show setting a text property using a delegate but I can't get them to work with the chart control.
could someone please help me?
delegate void SetTextCallback(string text);//assume text is the value and not the Text property
private void chartRefresh()
{
while (true)
{
//code to refresh chart
for (int i = 0; i < 30; i++)
{
if (this.chart1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.chart1.Series[0].Points.AddXY(i, i + 2);
}
chart1.Series[0].Points.AddXY(i, i + 2);
Thread.Sleep(500);
}
}
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.chart1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.chart1.Series[0].Points.AddXY(1, 4);
}
}
while (true)
{
for (int i = 0; i < 20000; i++)
{
Temp ++;
chart1 .Invoke((MethodInvoker)delegate
{
// Running on the UI thread
Series.Points.AddXY(i, random.Next(0,100));
});
}
You may find interesting the modern approach, using the Progress class. You can instantiate such an object in the UI thread, supplying the lambda function that will update the chart:
private IProgress<int> _chartProgress
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_chartProgress = new Progress<int>(i =>
{
this.chart1.Series[0].Points.AddXY(i, i + 2);
});
}
Then pass this object somehow to the background worker, and invoke its Report method any time you want the chart to be updated.
private void ChartRefresh(IProgress<int> progress)
{
while (true)
{
//code to refresh chart
for (int i = 0; i < 30; i++)
{
progress.Report(i);
Thread.Sleep(500);
}
}
}
The lambda will always run in the UI thread, because the Progress object was created in that thread.
The advantage of this technique is that it allows decoupling the UI-related stuff from the background-related work. It was invented primarily for facilitating reporting progress from asynchronous operations, but it can be useful in multithreading too.
I'm trying to have a seperate thread in a WinForms C# application start a background worker which controls a ProgressBar (marquee). The issue is that when i try to set the bar to visible it just does nothing, and i've tried many forms of Invoke but they don't seem to help.
The following method progressBarCycle is called from a separate thread.
BackgroundWorker backgroundWorker = new BackgroundWorker();
public void progressBarCycle(int duration)
{
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.RunWorkerAsync(duration);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(0);
DateTime end = DateTime.Now.AddMilliseconds((int)e.Argument);
while (DateTime.Now <= end)
{
System.Threading.Thread.Sleep(1000);
}
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!this.IsHandleCreated)
this.CreateHandle();
statusStrip1.Invoke((MethodInvoker)delegate
{
progressBar1.Visible = false;
});
// if (!this.IsHandleCreated)
// {
// this.CreateHandle();
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false));
// else progressBar1.Visible = false;
// }
// else
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false));
// else progressBar1.Visible = false;
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (!this.IsHandleCreated)
this.CreateHandle();
statusStrip1.Invoke((MethodInvoker)delegate
{
progressBar1.Visible = true;
});
// if (!this.IsHandleCreated)
// {
// this.CreateHandle();
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true));
// else progressBar1.Visible = true;
// }
// else
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true));
// else progressBar1.Visible = true;
}
Am I missing something obvious here? The comment sections are other things I've tried.
ProgressChanged is already raised on the UI thread (via the sync-context); your ProgressChanged does not need to do that Invoke - it can manipulate the UI directly (by contrast, DoWork can absolutely not do that). Perhaps the real problem is that you don't do any worker.ReportProgress(...) inside the loop - so it only happens once at the start.
Here's a full example:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using (var worker = new BackgroundWorker {
WorkerReportsProgress = true })
using (var progBar = new ProgressBar {
Visible = false, Step = 1, Maximum = 100,
Dock = DockStyle.Bottom })
using (var btn = new Button { Dock = DockStyle.Top, Text = "Start" })
using (var form = new Form { Controls = { btn, progBar } })
{
worker.ProgressChanged += (s,a) => {
progBar.Visible = true;
progBar.Value = a.ProgressPercentage;
};
worker.RunWorkerCompleted += delegate
{
progBar.Visible = false;
};
worker.DoWork += delegate
{
for (int i = 0; i < 100; i++)
{
worker.ReportProgress(i);
Thread.Sleep(100);
}
};
btn.Click += delegate
{
worker.RunWorkerAsync();
};
Application.Run(form);
}
}
}
Run progressBarCycle from the UI thread. RunWorkerAsync will
create the new thread for you.
In backgroundWorker_ProgressChanged simply call
progressBar1.Visible = true;. There is no need for Invoke.
Better also add a progressBar1.Refresh(); .
Another possibility to be aware of is that the progress bar is running on your UI thread. In order for the progress bar to be displayed and redraw itself to show new progress amounts, the UI thread must be running freely, processing windows messages in the main application loop.
So if you start your background worker thread but then your UI thread sits in a busy wait loop waiting for it to complete, or goes off and does loads of other work, then it won't be processing windows messages and your progress bar will be "unresponsive". You need to release the UI thread so that this updating still happens (i.e. return from your event handler and allow the UI to continue running as normal).
The danger of this is that if the UI is active, then the user can still interact with it. You therefore have to write the UI to be aware when the background worker is active, and handle the situation properly (problems can include: Allowing the user to start the background worker again while it is already running, UI trying to display information while the worker thread is busily updating it, the user deciding to load a new document or quit while the background worker is busy, etc). The two main solutions to this are to wrap every bit of UI in a protective shield that stops anything dangerous being initiated while the background work is running (this can be a lot of work if you have lots of controls to wrap in this way, and it's easy to make a mistake that lets a bug slip through) or to leave the UI "unprotected" but add an IMessageFilter that stops all "dangerous" user interaction (clicks and keypresses) by suppressing their incoming windows messages (WM_KEYPRESS etc) while the background processing is active.
I have a simple app which fires of a series of data intensive tasks. I'm not very experienced with WinForms and I was wondering the best way to do this without locking the interface. Can backgroundWorker be re-used, or is there another way to do this?
Thanks
BackgroundWorker is a thread that also includes notification synchronization. For example, if you wanted to update your UI when the scan completes, a regular Thread cannot access the UI objects (only the UI thread can do that); so, BackgroundWorker provides a Completed event handler that runs on the UI thread when the operation completes.
for more info see: Walkthrough: Multithreading with the BackgroundWorker Component (MSDN)
and a simple sample code:
var worker = new System.ComponentModel.BackgroundWorker();
worker.DoWork += (sender,e) => Thread.Sleep(60000);
worker.RunWorkerCompleted += (sender,e) => MessageBox.Show("Hello there!");
worker.RunWorkerAsync();
backgroundWorker can be used.
its benefit - it allows you to update a progress bar and interact with UI controls. (WorkerReportsProgress)
Also it has a cancellation mechanism. (WorkerSupportsCancellation)
You can use BackgroundWorker for such requirements. Below is a sample which updates a label status based on percentage task [long running] completion. Also, there is a sample business class which sets some value and the value is set back to UI via ProgressChanged handler. DoWork is the place where you write your long running task logic. Copy-Paste the code below after adding a label and backgroundworker component on a Winforms app & give it a shot. You may debug across various handler [RunWorkerCompleted, ProgressChanged, DoWork] and have a look at InitWorker method. Notice the cancellation feature too.
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
BusinessClass _biz = new BusinessClass();
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
int junk = 0;
for (i = 0; i <= 199990000; i++)
{
int result = _biz.MyFunction(junk);
junk++;
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// note I can pass the business class result also and display the same in the LABEL
_worker.ReportProgress(percentComplete, result);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
}
else if (e.Error != null)
{
// Do something with the error
}
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
}
public class BusinessClass
{
public int MyFunction(int input)
{
return input+10;
}
}
}
The background worker would be a good choice to start with
For more info look here
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
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++));