I'm creating a form which simulates a progress bar.
The form has a progress bar, a label with a title and another label showing the progress percentage.
The idea is that before starting a loop I create an instance of the form using that loop's element count. Each iteration of the loop a method is called to update the progress bar, and the percentage label.
The progress bar is being updated fine, but the title and percentage labels are not being updated. I've read that's because everything is executed in the same thread, and the UI is not being updated.
This is my code:
Main program
...
MyProgressBar_Form pb = new MyProgressBar_Form("Test of progress bar", selectionBudget.Count);
pb.Show();
foreach (SelectedObject obj in selectionBudget) {
pb.meterProgress();
...
}
pb.Close();
...
Form class
public partial class MyProgressBar_Form : Form {
public MyProgressBar_Form(string title, int elems) {
InitializeComponent();
titleLbl.Text = title;
progressBar1.Minimum = 0;
progressBar1.Maximum = elems;
progressBar1.Step = 1;
this.Refresh();
}
public void meterProgress() {
if (progressBar1.Value < progressBar1.Maximum) {
progressBar1.Value++;
double percentage = Math.Round((double)(progressBar1.Value / progressBar1.Maximum * 100), 2);
progressLbl.Text = $"{percentage}%";
this.Refresh();
}
}
}
Try Application.DoEvents() inside the loop.
Related
hope anybody can help me. My problem is the progress bar in C# WinForms. I have the following Code:
(There is a stupid calculate from an uint until a given number from a textbox and i want to show the progress while the calculate method is running)
// The stupid method which calculate
public void ueberlaufUint()
{
try
{
uint ueberlaufZahl = Convert.ToUInt32(textBox1.Text);
do
{
ueberlaufZahl++;
//Console.WriteLine(ueberlaufZahl);
} while (ueberlaufZahl <= 100);
label1.Text = "Endzahl: " + ueberlaufZahl;
}
catch (Exception)
{
MessageBox.Show("Only not negative natural numbers accepted");
}
}
// Buttonclickevent
private async void button1_Click(object sender, EventArgs e)
{
ueberlaufUint();
progressBar1.Maximum = 100;
progressBar1.Step = 1;
var progress = new Progress<int>(v =>
{
// This lambda is executed in context of UI thread,
// so it can safely update form controls
progressBar1.Value = v;
});
// Run operation in another thread
await Task.Run(() => DoWork(progress));
}
// DoWork
public void DoWork(IProgress<int> progress)
{
// This method is executed in the context of
// another thread (different than the main UI thread),
// so use only thread-safe code
for (int j = 0; j < 10000; j++)
{
ueberlaufUint();
// Use progress to notify UI thread that progress has
// changed
if (progress != null)
progress.Report((j + 1) * 100 / 100000);
}
}
The progressbar only counts few steps with no dependency (in my meaning) with the calculate method.
Very great thanks in forward, sorry for my bad english.
Just a typo. You have an extra 0 in the code:
progress.Report((j + 1) * 100 / 100000);
should be
progress.Report((j + 1) * 100 / 10000);
I don't know how to implement a method with a separate thread using the BackgroundWorker in WinForms.
I want this method (after every click on a button) to perform:
create ProgressBar (each new one under the previous one)
create Bitmap and BackgroundWorker
set color of every pixel in that Bitmap in the separate thread using BackgroundWorker
display a precentage progress on the ProgressBar
after completing: create a new form with bitmap on the background
after completing: remove the ProgressBar
My code:
List<BackgroundWorker> Workers;
List<ProgressBar> Progress;
int OperationsCount = 0;
private void ShowProgress(int n, int percent)
{
Progress[n].Value = percent;
}
private void Blend(object sender, DoWorkEventArgs e)
{
Bitmap BlendedImage = ... // creates a bitmap
for (int i = 0; i < BlendedImage.Width; i++)
{
for (int j = 0; j < BlendedImage.Height; j++)
{
... //changing colour of every pixel
}
this.Invoke(new Action(()=>ShowProgress((int)e.Argument, (int)(100 * (double)(i/BlendedImage.Width)))));
}
Form BlendedImage_Form = new Form();
BlendedImage_Form.Size = new Size(BlendedImage.Width, BlendedImage.Height);
BlendedImage_Form.BackgroundImage = BlendedImage;
BlendedImage_Form.BackgroundImageLayout = ImageLayout.Stretch;
this.Invoke(new Action(() => BlendedImage_Form.Show()));
}
private void PerformBlending_Button_Click(object sender, EventArgs e)
{
int n = OperationsCount++;
Progress.Add(new ProgressBar());
Progress[n].Size = ...
Progress[n].Location = ...
Progress[n].Maximum = 100;
this.Controls.Add(Progress[n]);
Workers.Add(new BackgroundWorker());
Workers[n].DoWork += new DoWorkEventHandler(Blend);
Workers[n].RunWorkerCompleted += (object _sender, RunWorkerCompletedEventArgs _e) =>
{
//OperationsCount--;
//Progress[n].Dispose();
//this.Controls.Remove(Progress[n]);
//Progress.RemoveAt(n);
};
Workers[n].RunWorkerAsync(n);
}
When I click the button only once then everything seems to be good but when I click the button two times then program:
creates the first ProgressBar which shows progress correctly and the new form and bitmap are displayed also correctly
creates the second ProgressBar but it doesn't show the progress at all and no form and no bitmap is displayed.
PS I'd rather use BackgroundWorker than other tools.
As per your comment here is the solution
public void DoSomething()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(()=> DoSomething()));
}
else
{
// update the ui from here, no worries
}
}
In this code, I am modifying the object on main thread. If the calls made from non-UI thread this will goes in InvokeRequired.
// From this code you given in comment
https://pastebin.com/45jQXCt9
Please try with making instance inside invoke. It should work.
In C# im trying to make a little game type program and im trying to make a loading bar that uses the Progress bar and the text is using a Label, for example the Progress bar is 1 - 25 and i want the label text to update while the bar is, heres an example:
private void StartLoading_Click(object sender, EventArgs e)
{
MainProgressBar.Maximum = 25;
int P = 0;
while (P < 25)
{
// Delay
System.Threading.Thread.Sleep(130);
// Increase Progress
P++;
// Set Progress Bar Value
MainProgressBar.Value = P;
// Set Text Above Progress Bar
LoadingText.Text = P + "/25";
}
}
Ps. I dont want some Huge code, i want it to be simple like this
State of the art is this snippet for you:
private void StartLoading_Click(object sender, EventArgs e)
{
const int max = 25;
var progressHandler = new Progress<int>(value=>{
LoadingText.Text = value + "/" + max;
MainProgressBar.Value = value;
});
var progress = progressHandler as IProgress<int>;
await Task.Run(() =>
{
int P = 0;
while (P < 25)
{
Thread.Sleep(130);
progress?.Report(++P);
}
}
}
This processes your long running task (Sleep in this case), in a seperate Thread and reuse the value via the Progress-Class. This way your GUI is updated in GUI-Thread as recommended and you will get the updates accordingly. Further it's not recommended to use Application.DoEvents();, because there are many pitfalls you have to know about.
It has been much already written about. But seems to me not clear.
I need to modify ProgressBar in WinForm according the calculus beeing provided in parallel flow. Code is below:
namespace my_space
{
class myLongCalculus
{
static int iCount;
doSomeLongJob()
{
cycle1
{
cycle2
{
iCount += number
// Here should be ProgressBar in Form1 modified !
}
}
}
public class Form1 : Form
{
public Form1()
{
int iMax = 6;
ProgressBar pb = new ProgressBar();
pb.Max = Maximum;
for(int i=0, i<iMax; i++)
listOfObjects.Add(new myLongCalculus(i));
Parallel.For(0, iMax, i =>
{
listOfObjects[i].doSomeLongJob();
});
}
}
}
I tried various tricks threads, invoke, but with the same result:
after the first changing of ProgressBar parallel flow terminates and returns to calling program.
Any ideas will be appreciated
I have a method that shows when a process bar is in execution and when is successfully completed.
I worked fine, but I would like to add a percentage showing a 100% if is complete and less if it got stuck some where.
I have made several research online but I could not adapt anything to the solution that I am looking for.
This is my code:
private void progressBar()
{
int i;
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
for (i = 0; i <= 100; i++)
{
progressBar1.Value = i;
}
}
I use the method call on my execution button by calling it with the follow:
progressBar();
Thanks
I have adjust the prograssBar method with the following lines.
The solution works.
Thanks
int percent = (int)(((double)progressBar1.Value / (double)progressBar1.Maximum) * 100);
progressBar1.Refresh();
progressBar1.CreateGraphics().DrawString(percent.ToString() + "%",
new Font("Arial", (float)8.25, FontStyle.Regular),
Brushes.Black,
new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
In order to implement the progress in your operation, the operation's length must be calculated first. if it's not possible, you can't show a progress bar for that operation. (maybe only a loading gif)
but if so, There is an interface (IProgress) which can help you implement the progress reports.
First thing you should know, You must do the main task on another thread, and report the progress to the UI Thread. a simple example of this work would be something like this.
Progress.cs
public class Progress<T> : IProgress<T>
{
private readonly Action<T> _progressAction;
public Progress(Action<T> action)
{
_progressAction = action;
}
public void Report(T value)
{
_progressAction?.Invoke(value);
}
}
Your code would be like this, in which the task starts after you click a button named ButtonBase
Progress<int> MyProgressObject { get; set; }
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
MyProgressObject = new Progress<int>(ProgressAction);
ThreadPool.QueueUserWorkItem(TimeConsumingTask);
}
public void TimeConsumingTask(object state)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
MyProgressBar.Dispatcher.Invoke(() => ProgressAction(i));
}
}
public void ProgressAction(int progress)
{
MyProgressBar.Value = progress;
}
I know It might look difficult but this is the proper way of doing time consuming tasks and prevent UI block
If you use it as a part of backgroundworker it works perfectly
I added a Label in the middle of the progressbar
And i added last row in my bgw_ProgressChanged method
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
p_bar.Value = e.ProgressPercentage;
fnameLbl.Text = e.UserState.ToString();
percentLbl.Text = "%" + (e.ProgressPercentage).ToString();
}
ProgressPercentagevalue comes from the method below
foreach (var item in filebox1)
{
System.IO.File.Move(item, Path.Combine(destdir, Path.GetFileName(item)));
++counter;
int tmp = (int)((counter* 100) / totfiles);
bgw.ReportProgress(tmp, "File transfered : " + Path.GetFileName(item));
Thread.Sleep(100);
}
Totfiles is the number of files that I get from server.
Thread.Sleep(100) let's you see for a short time what is displayed with fnameLbl.Text
int total = ;
int val = ;
double createDivider = total / 100;
int percent = val / createDivider;
this value (percent) is the right percent '%' of total