Create and show progressBar with backgroundWorker - VS2013 - c#

I want show marquee progress bar in another thread of my app.
Here is my code:
bkgWorker->RunWorkerAsync();
private: System::Windows::Forms::ProgressBar^ progressBar;
private: System::Void bkgWorker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
progressBar = (gcnew System::Windows::Forms::ProgressBar());
progressBar->Location = System::Drawing::Point(548, 349);
progressBar->MarqueeAnimationSpeed = 15;
progressBar->Name = L"progressBar";
progressBar->Size = System::Drawing::Size(100, 23);
progressBar->Style = System::Windows::Forms::ProgressBarStyle::Marquee;
progressBar->TabIndex = 23;
progressBar->Show();
}
private: System::Void bkgWorker_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e) {
progressBar->Hide();
}
There is no fault, but I do not see the progress bar on my form.
What am I doing wrong ?
Thanks for help.

there are better and newer solutions replaced the old good background worker.
I suggest you to look at async await design.
Read this post: Reporting Progress from Async Tasks
The code should look something like this:
public async void StartProcessingButton_Click(object sender, EventArgs e)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var progress = new Progress<int>(percent =>
{
textBox1.Text = percent + "%";
});
// DoProcessing is run on the thread pool.
await Task.Run(() => DoProcessing(progress));
textBox1.Text = "Done!";
}
public void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i != 100; ++i)
{
Thread.Sleep(100); // CPU-bound work
if (progress != null)
progress.Report(i);
}
}

Related

Background worker is not reporting progress Winforms

I have a background worker running, which is dynamically making form fields from an xml file. Depending on the size of the xml, it takes some time to load, so I am using a loading bar to report the progress to use so they won't exit out of the program. The program works as intended, it hides the loading panel and shows the form fields when the worker finishes, but while loading, the loading bar won't load. I received no errors.
This is where the report progress is being called:
if (!retrievePath.Equals(""))
{
// create the template with the data from the file
XDocument filledDoc = templateCreator.CreateTemplateWithGivenData2(retrievePath, fileName2);
tempDoc = filledDoc;
XElement root = tempDoc.Root;
// get child forms of return data state and sections
IDataInterface dataInterface = new DataInterfaceImplementation();
IEnumerable<XElement> sections = dataInterface.GetSections(filledDoc);
// Grab forms that aren't empty
IEnumerable<XElement> forms = XmlClass.GetMefForms(filledDoc).Where(u => u.Value != "").ToList();
IEnumerable<XElement> extra = dataInterface.GetSections(filledDoc).Where(u => u.Value != "").ToList();
// get the return header state
elemForms = dataMiddleman.GetSections(filledDoc);
foreach (XElement el in elemForms)
{
if (el.Name.LocalName.Equals("ReturnHeaderState"))
{
createForms(el, 3);
}
}
foreach (XElement el in forms)
{
i = i + 1;
i = (i / forms.Count()) * 100;
if (i == 100)
{
i = (i / (forms.Count() - 1)) * 100;
}
createForms(el, i);
}
private void createForms(XElement x, int i)
{
this.Invoke((MethodInvoker)delegate {
backgroundWorker1.ReportProgress(i);
var pLabel = new ParentLabel(x);
this.leftGroup.Controls.Add(pLabel);
var parentPanel = new CustomPanel(x);
parentPanel.SendToBack();
this.thebox.Controls.Add(parentPanel);
RecursiveTraverse(x, parentPanel);
pLabel.Click += (sender, e) => PLabel_Click(sender, e);
pPanels.Add(parentPanel);
});
}
This is my background worker code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
loadingPanel.BringToFront();
populateNewFields();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
loadingBar.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
loadingBar.Value = 100;
Thread.Sleep(100);
loadingPanel.SendToBack();
loadingBar.Value = 0;
}
Your question is about Background worker is not reporting progress Winforms and I hope it's ok if I use a Minimal Reproducible Example to demo how to successfully fire an event when progress occurs on the background thread (which is is one way to achieve the outcome you want) and reducing the complex Xml operations to a "time-consuming black box" to be dealt with as a separate issue.
This Form will provide a means to test the notification using the MockCreateForm method which mimics a form creation by blocking the background worker for 5 ms. I believe your design spec is to send a notification every 100 operations.
Generic event lacks the needed properties so inherit EventArgs to customize the info received (declaring it outside the MainForm class).
public delegate void ProgressEventHandler(ProgressEventArgs e);
public class ProgressEventArgs : EventArgs
{
public ProgressEventArgs(int count, int total)
{
Count = count;
Total = total;
}
public int Count { get; }
public int Total { get; }
}
When the button (actually a CheckBox where Appearance=Button) state is toggled, it calls this worker Task using a CancellationTokenSource and CancellationToken so it can be halted. Every 100 times, the Progress event is fired:
private void btnWorker_CheckedChanged(object sender, EventArgs e)
{
if(btnWorker.Checked)
{
_cts = new CancellationTokenSource();
Task.Run(() =>
{
var formCount = 10000;
for (int i = 0; i < formCount; i++)
{
if(_cts.IsCancellationRequested)
{
return;
}
// Notify every 100 times.
if((i % 100) == 0)
{
Progress?.Invoke(new ProgressEventArgs(count: i, total: formCount));
}
MockCreateForm();
}
Progress?.Invoke(new ProgressEventArgs(count: formCount, total: formCount));
}, _cts.Token);
}
else
{
_cts.Cancel();
labelStatus.Text = "Idle";
}
}
CancellationTokenSource _cts = null;
The only thing left is to consume the event in the MainForm. The only thing that needs to be marshalled back onto the UI thread is the brief moment that Label.Text is being updated.
public MainForm()
{
InitializeComponent();
Progress += (e) =>
{
Invoke((MethodInvoker)delegate
{
labelStatus.Text = $"{e.Count} of {e.Total}";
});
};
}
public event ProgressEventHandler Progress;
What you do on the background thread is up to you. Just put it here:
public void MockCreateForm()
{
Task.Delay(5).Wait();
}
I hope this gets you closer to what you are trying to achieve.

Why ProgressBar does not displaying the current value / does not refreshing?

I use the following code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
progressBar1.Value = 0;
progressBar1.Step = 1;
progressBar1.Maximum = 100;
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
progressBar1.PerformStep();
label1.Text = (i + 1).ToString();
this.Refresh();
}
}
}
But, even after this.Refresh(); the value of the progress bar does not updated. Only the label updated. When the labels already show 100, for progress bar stil have more few steps to finish.
What i do wrong?
why the value of the progress bar is not updated?
How i should do it correct?
are you using Task, async, await? this is a common sample in winforms
see IProgress
public void DoWork(IProgress<int> progress)
{
// This method is executed in the context of
// another thread
for (int j = 0; j < 100000; j++)
{
//DO something
// Use progress to notify UI thread that progress has
// changed
if (progress != null)
progress.Report((j + 1) * 100 / 100000);
}
}
private async void button_Click(object sender, EventArgs e)
{
progressBar.Maximum = 100;
progressBar.Step = 1;
var progress = new Progress<int>(v =>
{
// This lambda is executed in context of UI thread,
// so it can safely update form controls
progressBar.Value = v;
});
// Run operation in another thread
await Task.Run(() => DoWork(progress));
}
I tried your code and it worked fine for me, did you add any special properties to your progress bar?
Assuming that it is all there is to it, try removing it and adding a new one without adjusting its default properties, you can also try adjusting the value in your Thread.Sleep() so that you can see the progress more

ReportProgress doesn't call progressChanged with tasks in c#

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.

Progress bar not incrementing

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();
}
}

Async Behaviour

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);
}
}

Categories

Resources