I have the following code which read a file and also increment the progress bar while reading it, but I don't see any activity in my progressBar. Why is this?
progressBar1.Minimum = 0;
progressBar1.Maximum = (int)fileStream.Length + 1;
progressBar1.Value = 0;
using (fileStream)
{
fileStreamLength = (int)fileStream.Length + 1;
fileInBytes = new byte[fileStreamLength];
int currbyte = 0, i = 0;
var a = 0;
while (currbyte != -1)
{
currbyte = fileStream.ReadByte();
fileInBytes[i++] = (byte)currbyte;
progressBar1.Value=i;
}
}
It is incrementing but you cannot see it. It is caused by running your loop in UI thread.
Look for BackGroundWorker or async/await pattern.
User Method Invoker to update the UI...
try this...
Do all the your work in a thread and when updating the progressbar use the following lines...
For Windows Forms
this.Invoke((MethodInvoker) delegate
{
progressBar1.value=i;
});
For WPF
Dispatcher.BeginInvoke(new Action(delegate
{
progressBar1.value=i;
}));
your best option will be Background Worker.
drag and drop a BackgroundWorker from toolbox. then you have to implement 2 function: one is doing the background work, another is for reporting to UI.
using System.ComponentModel;
using System.Threading;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
// Start the BackgroundWorker.
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// begin reading your file here...
// set the progress bar value and report it to the main UI
int i = 0; // value between 0~100
backgroundWorker1.ReportProgress(i);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString();
}
}
Related
Introduction
I am trying to make a WinForms app using .Net.
I am using tutorial from here wich shows BackgroundWorker and ProgressBar integration.
I added ProgressBar and BackgroundWorker controls to the form.
The names are the same as in example. Additionaly, i set WorkerReportProgress property to True for BackgroundWorker. No errors are shown and project compiles sucessfully...
Problem
The problem is - progressbar does not move.
And yet, it moves when clicked manually... progressBar1.PerformStep();.
What am i missing?
Code
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
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 500 milliseconds.
Thread.Sleep(500);
// Report progress.
BackgroundWorker1.ReportProgress(i);
}
}
private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString();
}
private void button2_Click(object sender, EventArgs e)
{
progressBar1.PerformStep();
}
}
}
Update
Removed progressBar1.PerformStep(); from DoWork and ProgressChanged.
Still the problem persists (ProgressBar does not move).
Thank you for ideas so far, will look into it more on Monday.
After you made sure you attached the event handlers to ProgressChanged and DoWork:
Remove progressBar1.PerformStep() from DoWork event handler.
Then use just progressBar1.Value = e.ProgressPercentage; in ProgressChanged event handler.
I wrote a simple Multithreading with Progress bar code a few years back. Hope it helps you:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
Normally when writing a UI Element from a Alterante Thread, you have to use Invoke. BackgroundWorker is nice and Invoking the "ReportProgress" and "RunWorkerCompleted" Events on the thread that created it (wich should be the GUI thread) so you do not have to deal with that part of Multithreading wonkyness yet.
It is also nice enough to catch any Exceptions that would normally escape DoWork and be swallowed, exposing them to you in the Completed Event Args. Swallowing Exceptions is a huge issue with Multithreading.
The core issue is, that your loop breaks due to a Exception. Calling progressBar1.PerformStep(); inside the DoWork Event has to throw a "CrossThreadException". The BackgroudnWorker finishes (due to an exception) instantly. The RunWorker completed event is triggered when i was just the initial value.
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();
});
}
I am creating some files from xml data in the background using
Task.Factory.StartNew(() => xmlconvert(xx, yy));
Now, the question is how to show the progress of this method using a StatusStrip control with some message and the progress or at least just a scrolling animation for the progress. I don't just have any idea how would it work.
Update:
First of all, this method 'xmlconvert(xx, yy)' has four different forms depends on the condition user selects at runtime.
In the main form of my application user can select from different conditions to process on the data. Then finally when user click on the Button 'Create' all these conditions are being checked and a suitable method will be called within that button click event. I need to show the progress of this method which is being invoked at runtime.
private void btnCreateRelease_Click(object sender, EventArgs e)
{
// Checks set of conditions
if(cond 1)
{
xmlconvert_1();
}
else if (cond2)
{
xmlconvert_2();
}
else if (cond3)
{
xmlconvert_3();
}
else if (cond4)
{
xmlconvert_4();
}
}
I want to show progress of one of these methods which will be invoked at runtime depends on the condition.
Thanks a lot.
You can use the BackgroundWorker for this, and it's pretty simple, too. Here's a sample to get you going:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
void Form1_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Do your work in here.
xmlconvert(xx, yy);
for (int i = 0; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
System.Threading.Thread.Sleep(100);
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
}
And here's the link to the documentation.
To get it to work in your scenario, I would suggest you add a Progress bar to your StatusStrip control and update it from within the backgroundWorker1_ProgressChanged event.
If you wish just to show, that your app is not hang may help following approach:
public static class ActionExtensions
{
public static void RunWithMargueProgress(this Action action)
{
var progressForm = new ProgressForm();
progressForm.Show();
Task.Factory.StartNew(action)
.ContinueWith(t =>
{
progressForm.Close();
progressForm.Dispose();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Where ProgressForm would be a simple form with ProgressBar, that is set to Marquee style. If you have idea, how it is progressing, it is better to show progress for user and use BackgroundWorker.
As long as it's parameter is Action, it is easily reusable.
Usage would be:
private void button_Click(object sender, EventArgs e)
{
Action action = () => Thread.Sleep(5000);
action.RunWithMargueProgress();
}
If you have control in status strip, that you wish to animate, you can do it like this:
public static void RunWithMargueProgress(this Action action, ToolStripProgressBar progressBar)
{
progressBar.Style = ProgressBarStyle.Marquee;
progressBar.MarqueeAnimationSpeed = 30;
Task.Factory.StartNew(action)
.ContinueWith(t =>
{
progressBar.MarqueeAnimationSpeed = 0;
progressBar.Style = ProgressBarStyle.Continuous;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Usage would be pretty much the same:
private void button_Click(object sender, EventArgs e)
{
Action action = () => Thread.Sleep(5000);
action.RunWithMargueProgress(ToolStripProgressBar);
}
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;
}
I have a strange case.
I have a combobox in my windows form application.
When selected index changed, it starts a background worker process to interact with progress bar.
My DoWork is like that :
private void backgroundWorkerProgressBar_DoWork(object sender, DoWorkEventArgs e)
{
int campaignID = (int)e.Argument;
for (int i = 0; i < 100; i++)
{
CampaignEmailIndex.TryGetValue(campaignID, out subscribers); // This is a dictionary that keeps subscriber numbers. Subscriber number keeps growing while application works.
backgroundWorkerProgressBar.ReportProgress(subscribers);
}
}
My Progress_Changed is simple :
private void backgroundWorkerProgressBar_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
But this code does not work properly. Progress bar does not move in the application unless I change selected index of combobox.
But this code works in DoWork event handler. It changes the state of the progress bar and does not need me to select combobox index again.
for (int i = 0; i < 100; i++)
{
backgroundWorkerProgressBar.ReportProgress(i);
}
When I debug the application, everything works properly, value is passed to progress changed event but it is not shown on UI. By the way CampaignEmailIndex and subsribers are global variables.
Thanks All.
Edited :
The code that starts the background worker is :
private void cmbCampaignInPoolByID_SelectedIndexChanged(object sender, EventArgs e)
{
int campaignID = (int)cmbCampaignInPoolByID.SelectedItem;
DataTable dt = DatabaseManager.GetCampaignDetails(campaignID);
subscribers = DatabaseManager.GetCampaignSubscribers(campaignID).Rows.Count;
progressBarMailQueu.Maximum = subscribers;
backgroundWorkerProgressBar.WorkerReportsProgress = true;
backgroundWorkerProgressBar.DoWork += new DoWorkEventHandler(backgroundWorkerProgressBar_DoWork);
backgroundWorkerProgressBar.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerProgressBar_ProgressChanged);
backgroundWorkerProgressBar.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerProgressBar_RunWorkerCompleted);
backgroundWorkerProgressBar.RunWorkerAsync(campaignID);
}
I tried to write your code:
private void cb_SelectedIndexChanged(object sender, EventArgs e)
{
bg.RunWorkerAsync(cb.SelectedIndex);
}
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
int campaignID = (int)e.Argument;
for (int i = 0; i < 100; i++)
{
int subscribers = new Random().Next(0, 100);
bg.ReportProgress(subscribers);
Thread.Sleep(30);
}
}
private void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pb.Value = e.ProgressPercentage;
}
Look to Thread.Sleep(30): it's used to be able to see something.
Everything works for me: the progressbar moves correctly during the for cycle.
bg is a BackgroungWorker with ReportProgress=true and pb is ProgressBar (0,100).
If you are sure that subscribers is in range 0..pb.Max that should work.