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();
Related
I've created a loading form within my C# WinForms application that is shown during long processes.
I've added a progress bar to my loading form that I would like to update to give some feedback of loading to the user.
Within my loading form code, I have created a new BackgroundWorker and added DoWork and ProgressChanged event handlers.
The problem is that the backgroundWorker_ProgressChanged is not being called. I have inserted break points into the function and they are not caught.
Where am I going wrong? Any help appreciated. Thanks.
frmLoading:
public frmLoading()
{
InitializeComponent();
//check if bg worker is null
if (backgroundWorker == null)
{
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
}
backgroundWorker.WorkerReportsProgress = true;
//start
backgroundWorker.RunWorkerAsync();
}
backgroundWorker_DoWork:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
backgroundWorker.ReportProgress(i);
}
}
backgroundWorker_ProgressChanged:
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//update value of progress bar
progressBar1.Value = e.ProgressPercentage;
//set the text of the progress bar
this.Text = e.ProgressPercentage.ToString();
}
Here's how you would do this with Progress<int> and Task.Run:
public frmLoading()
{
InitializeComponent();
IProgress<int> progress = new Progress<int>(p =>
{
//update value of progress bar
progressBar1.Value = p;
//set the text of the progress bar
this.Text = p.ToString();
});
Task.Run(async () =>
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(TimeSpan.FromSeconds(0.1));
progress.Report(i);
}
});
}
Personally I prefer Microsoft's Reactive Framework:
public frmLoading()
{
InitializeComponent();
Observable
.Interval(TimeSpan.FromSeconds(0.1))
.Take(100)
.ObserveOn(this)
.Select(p => p + 1)
.Subscribe(p =>
{
//update value of progress bar
progressBar1.Value = p;
//set the text of the progress bar
this.Text = p.ToString();
});
}
I have a button .It will open a file and process a file .I want to show the progress bar while processing the file .
when i am doing .its working
public MainframeDataExchangeTool()
{
InitializeComponent();
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.Visible = false;
_Random = new Random();
InitializeBackgroundWorker();
}
private void InitializeBackgroundWorker()
{
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.DoWork += (sender, e) => ((MethodInvoker)e.Argument).Invoke();
_BackgroundWorker.ProgressChanged += (sender, e) =>
{
_ProgressBar.Style = ProgressBarStyle.Continuous;
_ProgressBar.Value = e.ProgressPercentage;
};
_BackgroundWorker.RunWorkerCompleted += (sender, e) =>
{
if (_ProgressBar.Style == ProgressBarStyle.Marquee)
{
_ProgressBar.Visible = false;
}
};
}
In my button click i am doing
private void btnOpenScriptFile_Click(object sender, EventArgs e)
{
try
{
loadScriptFlDlg.Filter = Constants.SCRIPT_FILE_FILTER;
loadScriptFlDlg.FilterIndex = 3;
loadScriptFlDlg.RestoreDirectory = true;
loadScriptFlDlg.FileName = string.Empty;
DialogResult objDialogResult = loadScriptFlDlg.ShowDialog();
if (objDialogResult.Equals(DialogResult.OK))
{
_BackgroundWorker.RunWorkerAsync(new MethodInvoker(() =>
{
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
_BackgroundWorker.ReportProgress(i);
}
}));
EnableDisableControls("OpenScript");
string strScriptError = LoadScriptFromFile(loadScriptFlDlg.FileName);///loading will taking time but progress bar not showing
Basically progress bar is showing at the end of data load but not while loading the data
You cannot see the progress as UI thread cannot update UI because it is busy loading your file. You must call LoadScriptFromFile from the background worker and keep UI thread free to process events and update UI.
I used this in my project with 2 methode (exemple):
1* Invoke(new Action(() => _ProgressBar.Visible = true));
2* or use Application.DoEvents() after your _BackgroundWorker.ReportProgress(i);
Surprised the compiler didn't throw a hissy fit...because you need an extra }.
You open 4 levels of nesting, but only close 3.
I have modified your code as shown below:
private void btnOpenScriptFile_Click(object sender, EventArgs e)
{
try
{
if (objDialogResult.Equals(DialogResult.OK))
{
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
_ProgressBar.BeginInvoke(new Action(() => _ProgressBar.Value = i));
//Process my file here
}
}
}
Catch
{
}
}
I would always suggest reducing the number of empty lines, for readability.
I also find that the 'allman' style bracketing is easiest to debug.
But as always, each to his own.
EDIT:
After OP editted code:
Try adding:
application.doevents()
after your:
_BackgroundWorker.ReportProgress(i);
Why? The code will stay in the for loop until completes. By doing 'doevents' you are telling it to update externally, before the next loop.
Without the 'doevents' i guess you would see 0 and 100 only.
Simply do something like this with a BackgroundWorker (bgw).
private void MyMethod()
{
bgw.RunWorkerAsync(); //this calls the DoWork event
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
//Expensive task
//Calculate how far you through your task (ie has read X of Y bytes of file)
bgw.ReportProgress(myInteger);
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
Make sure you set the BackgroundWorker's "WorkerReportsProgress" property to True!
The following is my background worker thread
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread t1 = new Thread(Thread1);
t1.Start();
Thread t2 = new Thread(Thread2);
t2.Start();
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
}
}
Thread1 code is as follows
static void Thread1()
{
int nofiles=0;
int returned = checkforfolderthread(1);
int startvalue = 0;
int stopvalue = 5000;
if (returned == 1)
{
nofiles = countfiles();
startvalue = startvalue + (nofiles - 1) * 1000;
stopvalue = stopvalue - startvalue;
}
repeat(startvalue, stopvalue,1,nofiles-1);
}
Function called from a thread is as follows
static void repeat(int ini, int fin, int threadno, int startadd)
{
int i, j;
for (j = ini; j < ini + fin; j += 1000)
{
StringBuilder sb = new StringBuilder();
for (i = j; i < j + 1000; i += 100)
{
WebClient wc = new WebClient();
string add = System.String.Format("http://www.colourlovers.com/api/colors/new?numResults=100&resultOffset={0}", i);
try
{
string tobeadded = wc.DownloadString(add);
sb.AppendLine();
sb.Append(tobeadded);
}
catch (Exception)
{
break;
}
}
string folderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string filename = System.String.Format("DownloadPalette\\Thread{0}\\color{1}.xml",threadno,startadd);
string location = Path.Combine(folderpath, filename);
File.WriteAllText(location, sb.ToString());
startadd = startadd + 1;
}
}
What I would want to do is continuously update a progressbar after each for i loop is completed.
But I cannot access the progressbar from this function running in the background thread.
Please Help me
You miss this method..
// Back on the 'UI' thread so we can update the progress bar
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
According on this reference : BackgroundWorker and ProgressBar demo
You should use invoke, as described here: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
First create a method to update the progressbar(create it on the main thread GUI):
private void UpdateBar()
{
//your code to update
}
Then create a delegate and pass you method to it(this code is also for main thread GUI):
private delegate void UpdateProgressBarDelegate();
private UpdateProgressBarDelegate UpdateProgressBarDelegate_Object;
UpdateProgressBarDelegate_Object = new UpdateProgressBarDelegate(this.UpdateBar);
Now update it from another thread like this:
progressbar.Invoke(UpdateProgressBarDelegate_Object);
Here we are calling the delegate object which will call UpdateBar method on GUI thread with a safe thread call.
If you need to update more than just the progressbar value you could call a method and checking if an invoke is required. An invoke is required if you want to access a UI object from a separate thread.
private void updateProgress(object sender, int count, int total)
{
if (base.InvokeRequired)
{
base.Invoke(new ProcessCountHandler(this.updateProgress), new object[] { sender, count, total });
}
else if (count <= this.progressBar1.Maximum)
{
this.progressBar1.Value = count;
this.CompletedCount.Text = count.ToString("N0") + " of " + total.ToString("N0");
}
}
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++));