I have the following code to update the progress bar in async fashion and i notice
its async behaviour through the call to MessageBox.In this case it works perfectly
but when i give a sleep of 1s(1000) the MessageBox doesnot pops up and the the complete progress bar fills at once.
Kindly tell why this is happening.
private void button1_Click(object sender, EventArgs e)
{
Update_Async async = new Update_Async(Update_Async_method);
progressBar1.BeginInvoke(async,10);
MessageBox.Show("Updation In Progress");
}
public void Update_Async_method(int a)
{
this.progressBar1.Maximum = a;
for (int i = 1; i <= a; i++)
{
progressBar1.Value = a;
Thread.Sleep(10);
//Thread.Sleep(1000);
}
}
Try Update_Async.BeginInvoke(async, 10) instead if you want the delegate to run asynchrnously but, you'll have to cross thread checking on the update to the progress bar.
In response to your comment, very similar to what you are doing already,
void UpdatingFunction(int value)
{
if (this.progressBar.InvokeRequired)
{
this.progressBar.BeginInvoke(UpdatingFunction, value);
return;
}
// Invoke not required, work on progressbar.
}
This also explains what the Invoke methods on controls are for.
Delegate.BeginInvoke will run a method in a thread once and then dispose it. It is a poor choice if you want to repeatedly do some work in a thread and return intermediate results. If that is what you want, you should use BackgroundWorker. Highly abbreviated snippet:
BackgroundWorker bw;
YourFormConstructor()
{
...
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += BackgroundCalculations;
bw.ProgressChanged += ShowBackgroundProgress;
}
private void button1_Click(object sender, EventArgs e)
{
bw.RunWorkerAsync(10);
}
void ShowBackgroundProgress(object sender, ProgressChangedEventArgs e)
{
this.progressBar.Value = e.ProgressPercentage;
}
static void BackgroundCalculations(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
int max = (int)e.Argument;
for (int i = 0; i < max; i++)
{
bw.ReportProgress(i * 100 / max);
Thread.Sleep(10);
}
bw.ReportProgress(100);
}
}
Related
I want to create a basic multi-thread application using a progress bar. Meaning that this progress bar will run on a different thread while the main thread is busy in the large process it is doing. I've seen a lot of tutorials about it. But the thing that they are multi-threading is the one that doing the large process. The progress bar in the other form is just showing a simple progress bar that runs and complete using a timer.
This is the code I have now.
For the thread:
public void thread()
{
Form6 for6 = new Form6();
for6.Show();
}
TH1 = new Thread(thread);
TH1.Start();
For the progress bar (Code inside form 6)
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(+1);
if (progressBar1.Value == 99)
{
this.Close();
}
}
private void Form6_Load(object sender, EventArgs e)
{
timer1.Start();
}
My problem is the thread in here doesn't run the Form6. Is there any way for me to do this?
Instead of using main thread for large processing you can use the Background worker for all the processing.
Here's a simple example to do it.
public partial class Form1 : Form
{
BackgroundWorker bgw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
label1.Text = "";
label2.Text = "";
}
private void button1_Click_1(object sender, EventArgs e)
{
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.WorkerReportsProgress = true;
bgw.RunWorkerAsync();
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
int total = 57; //some number (this is your variable to change)!!
for (int i = 0; i <= total; i++) //some number (total)
{
System.Threading.Thread.Sleep(100);
int percents = (i * 100) / total;
bgw.ReportProgress(percents, i);
//2 arguments:
//1. procenteges (from 0 t0 100) - i do a calcumation
//2. some current value!
}
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label1.Text = String.Format("Progress: {0} %", e.ProgressPercentage);
label2.Text = String.Format("Total items transfered: {0}", e.UserState);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do the code when bgv completes its work
}
}
Instead of the ProgressBar, you should really move your long-running, non-UI code into a separate thread. The standard and easier way of doing this in WinForms is to use BackgroundWorker component, which can raise ProgressChanged event where you can update your UI. Important to note that ProgressChanged event is raised on the UI thread, not on the worker thread, so you don't even need to use Invoke() to perform UI operations (such as updating your ProgressBar).
you must use Control.Invoke for avoid cross-threading problem,but I prefer use BackgroundWorker for resolve it, create Form6 on a _field and use progressbar in ProgressChanged event for more information see this page
public void thread()
{
Form6 for6=null;
Application.OpenForms[0].Control.Invoke(delegate{
for6 = new Form6();
for6.Show();
});
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int currentProgress=-1;
while (currentProgress<length)
{
currentProgress=Worker.progress;
backgroundWorker1.ReportProgress(currentProgress);
Thread.Sleep(500);
length = Worker.UrlList.Count;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int ix = e.ProgressPercentage;
progressBar1.Value = ix;
lblText.Text =ix+" %";
}
I wrote a program to download page sources by reading a file have about 1000 URLs. so I used Tasks to download pages async. here Worker.progress is the currently executed URL amount. though the debuger hits the backgroundWorker1.ReportProgress(currentProgress); it never enter to the backgroundWorker1_ProgressChanged.
private void StartButton_Click(object sender, EventArgs e)
{
t.makeUrlList(inputFile);
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerAsync();
t.RunTasks();
Application.Exit();
}
background worker initializes when start button clicks...
here is where my tasks are created....
public void RunTasks()
{
if (numOfTasks > UrlList.Count)
numOfTasks=UrlList.Count-1;
Task[] t = new Task[numOfTasks];
int j = 0;
while ( j < UrlList.Count-1)
{
for (int i = 0; (i < t.Count())&&(j<UrlList.Count-1); i++)
{
try
{
if (t[i].IsCompleted || t[i].IsCanceled || t[i].IsFaulted)
{
t[i] = Task.Run(() => FindWIN(j));
j++;
progress = j;
}
}
catch (NullReferenceException ex)
{
t[i] = Task.Run(() => FindWIN(j));
j++;
progress = j;
}
}
}
}
If you want to BackgroundWorker supports updating progress information, the value of WorkerReportsProgress should be set to true . If this property is true , the user code can call ReportProgress for initiating event ProgressChanged .
Background worker initialization:-
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork+=backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged+=backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerAsync();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int currentProgress = -1;
decimal length=1000;
while (currentProgress < length)
{
currentProgress = Worker.progress;
backgroundWorker1.ReportProgress(currentProgress);
Thread.Sleep(500);
length = Worker.UrlList.Count;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
int ix = e.ProgressPercentage;
progressBar1.Value = ix;
lblText.Text = ix + " %";
}
See the demo code below. This is mostly untested, and certainly isn't 'production standard', but it should give you a good start!
It uses a ConcurrentQueue to hold the list of URLs to be processed. This is threadsafe, and makes life a lot easier.
It has a configurable number of urls and tasks. It's best not to make 1000 tasks, but instead have a queue of work items, and a smaller pool of Tasks which 'pull items' off the queue until it's empty. This means you can performance test different numbers of Tasks and find the best value for your problem.
It uses Invoke when updating the progress bar - this avoids the cross-thread exception.
No BackgroundWorker - just TaskFactory and Task
public partial class Form1 : Form
{
private const int UrlCount = 1000;
private const int taskCount = 10;
private ConcurrentQueue<string> urlList;
private List<Task> taskList;
public Form1()
{
InitializeComponent();
}
private void ResetQueue()
{
// fake up a number of strings to process
urlList = new ConcurrentQueue<string>(Enumerable.Range(0, UrlCount)
.Select(i => "http://www." + Guid.NewGuid().ToString() + ".com"));
}
private void button1_Click(object sender, EventArgs e)
{
ResetQueue();
var taskFactory = new TaskFactory();
// start a bunch of tasks
taskList = Enumerable.Range(0, taskCount).Select(i => taskFactory.StartNew(() => ProcessUrl()))
.ToList();
}
void ProcessUrl()
{
string current;
// keep grabbing items till the queue is empty
while (urlList.TryDequeue(out current))
{
// run your code
FindWIN(current);
// invoke here to avoid cross thread issues
Invoke((Action)(() => UpdateProgress()));
}
}
void FindWIN(string url)
{
// your code here
// as a demo, sleep a sort-of-random time between 0 and 100 ms
Thread.Sleep(Math.Abs(url.GetHashCode()) % 100);
}
void UpdateProgress()
{
// work out what percentage of the queue is processed
progressBar1.Value = (int)(100 - ((double)urlList.Count * 100.0 / UrlCount));
}
}
You should set WorkerReportsProgress property of your worker to true on initialization stage.
I am trying to Design a WinForms control in C# which will get some data from a database while it's loading.
I want to use a progress bar to show the progress.
I tried this code (and also many others):
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Show();
progressBar1.Value = 10;
int n;
n = 50;//number of records in DB ,
double progress = 0;
double progstep = 25 / n;
for (int i = 1; i <= n; i++)
{
//getting
if (progress <= 100)
progressBar1.Value = (int)progress;
}
progressBar1.Value = 35;
n = 100;//number of records in DB for another data reading from DB ,
progress = 35;
progstep = 65 / n;
for (int i = 1; i <= n; i++)
{
//getting data from DB
dataGridView1.Rows.Add(....);
//Adding that data to a datagrid -- parametrs removed.
progress += progress;
if (progress <= 100)
progressBar1.Value = (int)progress;
}
}
But, the problem is that the form will wait until data reading progress is completed, and I can see just a full progress bar and all data loaded.
What should I do to fix this?
Since this is winforms, i'd recommend using a BackgroundWorker.
Basic example:
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new DoWorkEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerAsync(//pass in object to process)
Which would then kickoff:
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
//Do all of your work here
bgWorker.ReportProgress(); //percent done calculation
}
Then the Progress changed event would fire to update the UI safely:
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
Add a backgroundWorker1 to your form.
Then add a YourForm_Shown event
private void YourForm_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
Add on form's constructor after InitializeComponent()
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts.
backgroundWorker1.DoWork += new
DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress.
backgroundWorker1.ProgressChanged += new
ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
And last add the voids of backgroundWorker1:
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
And:
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= n; i++)
{
//getting data from DB.
dataGridView1.Rows.Add(....);
//Adding that data to a datagrid -- parametrs removed.
backgroundWorker1.ReportProgress(i);
// Simulate long task
}
}
This is simple mockup to show you how to work with background worker:
First in your OnLoad create background worker and attach 2 events to it:
var bw = new BackgroundWorker();
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(WorkCompleted);
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerAsync(data); // Assume data is list of numbers.
private void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
// After work completed remove event handlers and dispose.
{
var bw = (BackgroundWorker)sender;
bw.RunWorkerCompleted -= WorkCompleted;
bw.DoWork -= DoWork; bw.Dispose();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
var data = (List<int>)e.Argument;
foreach (var number in data)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke((MethodInvoker)delegate
{ this.ProcessNumber(number); });
}
else
{
ProcessNumber(number);
}
}
}
private void ProcessNumber(int i)
{
progressBar1.PerformStep();
//do something with i
}
Take a look at BackgroundWorker control. During form load invoke;
backgroundWorker.RunWorkerAsync();
and override event DoWork to do the dirty work (load data from database) and ProgressChanged to update progress bar. In the event body (lets say the event signature will be something like this):
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
var worker = (BackgroundWorker)sender;
// time consuming operation
worker.ReportProgress(10, null);
// ... another stuff
}
private void backgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
I am using a background worker to update some tables in sqlserver. the progressbar max is getting set to the correct value, the progressbar value is being incremented, the backgroundworker progresschanged is being called correctly with correct value, yet the bar is not progressing.
here is the code for the form
in the background_dowork method there is a loop which calls updateProgressBarValue which works with correct values.
public InterfaceConvertLonLat()
{
InitializeComponent();
Shown += new EventHandler(Form1_Shown);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
}
public void ConvertLonLat_Load(object sender, EventArgs e)
{
}
public void updateProgressBarValue()
{
progressBar1.Value++;
backgroundWorker1.ReportProgress(progressBar1.Value);
}
public void setProgressBarMax(int max)
{
progressBar1.Maximum = max;
MessageBox.Show("setprogressbarmax " + max);
}
public void Form1_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
convert.OSGB36ToWGS84("paf");
}
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
here is the loop contained in another class which calls updateprogressbarvalue, this is being fired and as stated backgroundworker1_progressChanged is being fired but the bar is not moving.
con.setProgressBarMax(address.Tables[0].Rows.Count);
foreach (DataRow LonLat in address.Tables[0].Rows)
{
con.updateProgressBarValue();
Double lon = 0;
Double lat = 0;
lat = Convert.ToDouble(LonLat["LTO"]);
lon = Convert.ToDouble(LonLat["LGO"]);
LocalToWGS84(ref lat, ref lon, OGB_M);
cmd1.Parameters["#LTW"].Value = lat;
cmd1.Parameters["#LGW"].Value = lon;
string dbQuery1 = "update " + tableName + " set LTW = #LTW, LGW = #LGW";
cmd1.CommandText = (dbQuery1);
cmd1.CommandType = CommandType.Text;
cmd1.Connection = conn;
cmd1.ExecuteNonQuery();
}
}
catch (Exception e)
{
MessageBox.Show("error converting: " + e.Message);
}
finally
{
conn.Close();
}
When reporting progress you need to fire an event from the background worker to tell the progressbar it needs updating. This can be done using the below:
backgroundWorker1.ReportProgress(10);
Change the value to what you need to demonstrate increased progress in your code. The progress changed event will mostly run on the same thread as your GUI so you should have no cross thread issues. One exception is if your form is being called from Excel via addin in which case Excel will be on the main thread.
You've got a number of issues - Your UpdateprogressBarValue() just increases the value of the progress bar but doesn't keep track of how many times its been called / what the current value is - so if you call it 101 times (assuming a range of 0-100), you'll get an OutOfRangeException
Your DoWork() method doesn't seem to call the update at all (either directly or by raising an event).
You can use events to do this but you're better off using delegates or perhaps anonymous functions. Something like...
public void setProgress(int value) {
progressBar1.invoke(delegate{ progressBar1.Value = value; }
}
then just call setProgress(0) through setProgress(progressBar1.MaxValue) from your DoWork() method
The following is seriously wrong:
public void updateProgressBarValue()
{
progressBar1.Value++; // not thread-safe
backgroundWorker1.ReportProgress(progressBar1.Value);
}
ReportProgress() is intended to be called form DoWork, it should not even read a Control property.
You should maintain a counter in the foreach loop and feed that to the progress mechanism.
Now this does not directly indicate why it doesn't move but you do not have a Completed handler. Are you sure the process finishes at all?
If an exception escapes your DoWork you will never know what happened.
private void Form1_Load(object sender, System.EventArgs e)
{
// Start the BackgroundWorker.
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
// Wait 100 milliseconds.
Thread.Sleep(100);
// Report progress.
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, progress e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString();
}
I think this might be caused by the fact that the UI in windows forms is running in its own thread/context. In this case you probably need to use Invoke to make adjustments to the UI.
Something like this:
public void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Invoke(new ProgressDelegate(UpdateProgress), e.ProgressPercentage);
}
delegate void ProgressDelegate(decimal value);
private void UpdateProgress(decimal value)
{
progressBar1.Value = value;
}
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
BackgroundWorker bgWorker;
Action<int> myProgressReporter;
public Window1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += bgWorker_Task;
bgWorker.RunWorkerCompleted += myWorker_RunWorkerCompleted;
// hook event to method
bgWorker.ProgressChanged += bgWorker_ReportProgress;
// hook the delegate to the method
myProgressReporter = updateProgress;
bgWorker.WorkerReportsProgress = true;
}
private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
object result;
result = e.Result;
MessageBox.Show(result.ToString());
progressBar1.Value = 0;
button1.IsEnabled = true;
}
private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
//Dispatcher.BeginInvoke(myProgressReporter, DispatcherPriority.Normal, e.ProgressPercentage);
}
private void updateProgress(int progressPercentage)
{
progressBar1.Value = progressPercentage;
}
private void bgWorker_Task(Object sender, DoWorkEventArgs e)
{
int total = 1000;
for (int i = 1; i <= total; i++)
{
if (bgWorker.WorkerReportsProgress)
{
int p = (int)(((float)i / (float)total) * 100);
bgWorker.ReportProgress(p);
}
Thread.Sleep(1); // Without Thread.Sleep(x) the main thread freezes or gives stackoverflow exception,
}
e.Result = "Completed";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if(!bgWorker.IsBusy)
bgWorker.RunWorkerAsync("This is a background process");
button1.IsEnabled = false;
}
}
}
Because in your (artificial) scenario you pump 1000 request-for-update to the main thread.
It gets no time to do an idle loop (necessary to do a screen update).
But (thanks to TerrorAustralis), you should start with merging your bgWorker_ReportProgress and myProgressReporter methods. You are now synchronizing twice, a possible cause of stackoverflow. Dispatching the UpdateProgress events is one of the main features of the Backgroundworker:
private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
//System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
//disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
progressBar1.Value = progressPercentage; // safe because we're on the main Thread here
}
Posibility:
Dispatcher.BeginInvoke() is an Asynchronous operation. Since this is the case, you are able to try to hit it again before it completes its operation. To see if this is the problem, try Dispatcher.Invoke() which is Synchronous
As a possible workaround, if you just want to update the progress bar, the backgroundWorker ProgressChanged event could do this without the use of an explicit dispatcher.