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.
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 have a long running WCF service and a client that consumes it via WPF. Am using a Progress Bar to notify the client of the percentage completion for a particular process (a method in WCF: I need to be able to display the percentage based on the looping counter in the service)
I have used Background Worker to display progress percentage but it does not display the progress correctly. (displays just 0 and 100 not the in between values) Everything works fine in DEBUG mode but not in RELEASE mode! (Progress bar is updated sequentially in DEBUG mode)
I tried using callbacks/wsDualHttpBinding but have some difficulty in getting this incorporated for all clients. So, had to drop this option.
working on async/await
I have googled quite a few links but nothing helps with my problem.
Please guide me on how to get the current/running value from a method that is not complete yet from a WCF service so I could populate the progress bar percentage based on this value. (in between values)
P.S: WCF service uses wsHttpBinding
sample code below:
public Progress()
{
// Start the BackgroundWorker.
myBGWorker.WorkerReportsProgress = true;
myBGWorker.WorkerSupportsCancellation = false;
myBGWorker.DoWork += myBGWorker_DoWork;
myBGWorker.ProgressChanged += myBGWorker_ProgressChanged;
}
public void ShowProgress()
{
myBGWorker.RunWorkerAsync();
}
private void myBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
// fetches a static value from the service
string value = _client.Progress();
int p=0;
for (int i = 1; i <= 100; i++)
{
// Report progress.
p = Convert.ToInt32(_client.Progress());
_logger.Debug("Progress5:" + p.ToString());
myBGWorker.ReportProgress(p, i);
}
}
private void myBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Dispatcher.BeginInvoke(new Action(delegate
{
progressBar1.Value = e.ProgressPercentage;
}), DispatcherPriority.ContextIdle);
}
Just to understand how it could be implemented. This is part of my working code.
.xaml file:
<ProgressBar x:Name="ProgressBarCompare" HorizontalAlignment="Left" Height="20" Margin="10,157,0,0" VerticalAlignment="Top" Width="321"/>
Function with process:
private async void btnCompare_Click(object sender, RoutedEventArgs e)
{
ProgressBarCompare.Value = 0;
lblCompare.Content = "";
List<string> list1= (List<string>)Application.Current.Properties["list1"];
List<string> list2= (List<string>)Application.Current.Properties["list2"];
List<Result> output = new List<Result>();
List<Result> passed = new List<Result>();
int topCount = emailList.Count;
int currentItem = 0;
int topBound = topCount - 1;
while (currentItem < topCount)
{
var hash = await CheckOperation(list1[currentItem]); // this line perform progress bar to be filled
var result = list2.Contains(hash);
//some operations
if (Convert.ToInt32(Math.Ceiling(100d * currentItem / topBound)) < 51)
{
Style style = this.FindResource("LabelTemplateNotFilled") as Style;
lblCompare.Style = style;
}
else
{
Style style = this.FindResource("LabelTemplateFilled") as Style;
lblCompare.Style = style;
}
ProgressBarCompare.Value = Convert.ToInt32(Math.Ceiling(100d * currentItem / topBound));
lblCompare.Content = Convert.ToInt32(Math.Ceiling(100d * currentItem / topBound)) + "%";
currentItem++;
}
lblCompare.Content = "COMPLETE";
}
and core function to that:
private async Task<string> CheckOperation(string input)
{
var result = "";
await Task.Run(() =>
{
//perform some code
});
return result;
}
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
So I'm trying to change the text from a WinForms project, from another class than the Form class.
It should work like this:
But instead it does this:
The way I used to do it is pass along the object as a parameter to my other class and from that other class I could change the text. I do the same with the progressbar and it does work there, so it's weird that it works with the progressbar but not the label.
I use this method to change the progressbar:
public void IncreaseProgress(int progBarStepSize, String statusMsg, int currentProject=-1) {
if (currentProject != -1) {
lblStatus.Text = String.Format("Status: {0} | project {1} of {2}",statusMsg,currentProject,ProjectCount);
}
else {
lblStatus.Text = String.Format("Status: {0}",statusMsg);
}
pb.Increment(progBarStepSize);
}
And here is where I use the method:
public void InitialiseFile(List<string> filePaths, int eurojobType)
{
foreach (string sheet in outputSheets) {
switch (sheet) {
case "Summary":
for (int i = 0; i < filePaths.Count; i++) {
var filePath = filePaths[i];
IncreaseProgress(1, "Reading Summary", i);
worksheetIn = excelReader.ReadExcelSummary(filePath);
IncreaseProgress(1, "Writing Summary", i);
excelWriter.WriteExcelSummary(worksheetIn);
}
break;
case "Monthly_Cat1":
for (int i = 0; i < filePaths.Count; i++) {
var filePath = filePaths[i];
IncreaseProgress(1, "Reading Monthly", i);
worksheetIn = excelReader.ReadExcelMonthly(filePath);
IncreaseProgress(1, "Writing Monthly", i);
excelWriter.WriteExcelMonthly(worksheetIn);
}
break;
}
}
IncreaseProgress(1, "Completed!");
}
Now I know this code works because the progressbar increments. And it should jump in the first if-loop because i gets passed as a parameter, which is never -1.
//manager class
private Label lblStatus;
private ProgressBar pb;
public Manager(ProgressBar pb, Label lbl){
this.pb = pb;
lblStatus = lbl;
}
//Form class
Manager mgr = new Manager(progressBar1, lblStatus, projectFilePaths.Count, outputSheets.ToArray(), exportPath);
mgr.InitialiseFile(projectFilePaths, eurjobType);
You can call lblStatus.Refresh(); to force the control to be redrawn, after setting its Text.
But consider Slaks comment:
Don't do blocking work on the UI thread
You can consider using BackgroundWorker or Task.Run or async/await pattern instead.
As an example:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
for (int i = 0; i < 10000; i++)
{
this.Invoke(new Action(() =>
{
label1.Text = i.ToString();
label1.Refresh();
}));
}
});
}
This way the numbers increase, the label refreshes and shows the new value, while the UI is responsive and for example you can move your form or click on other button.
You should put your UI related codes in an action fired by Invoke to prevent receiving cross thread operation exception.
I have written a user control using C# Winforms. In the user control, I have three textboxes:
txtStartNumber - input is of type: int.
txtEndNumber - input is of type: int.
txtQuantity - iput is of type: int. (value = txtEndNumber - txtStartNumber)
The progress bar denotes the no. of records added to the database and its total range is set to be equal to txtQuantity.
When one or more records are duplicate, the progress bar is stopped.
My questions are:
How to set the initial value of the progress bar?
How to manage the progress shown by progress bar?
How I save it to the database:
for (long i = from; i < to; i++)
{
for (int j = 0; j < (to - from); j++)
{
arrCardNum[j] = from + j;
string r = arrCardNum[j].ToString();
try
{
sp.SaveCards(r, 2, card_Type_ID, SaveDate, 2);
progressBar1.Value = j;
}
}
}
Try this:
private void StartBackgroundWork() {
if (Application.RenderWithVisualStyles)
progressBar.Style = ProgressBarStyle.Marquee;
else {
progressBar.Style = ProgressBarStyle.Continuous;
progressBar.Maximum = 100;
progressBar.Value = 0;
timer.Enabled = true;
}
backgroundWorker.RunWorkerAsync();
}
private void timer_Tick(object sender, EventArgs e) {
progressBar.Value += 5;
if (progressBar.Value > 120)
progressBar.Value = 0;
}
The Marquee style requires VisualStyles to be enabled, but it continuously scrolls on its own without needing to be updated. I use that for database operations that don't report their progress.
Here is another Progress Bar Tutorial
You can't use loop to do this with progressbar. There is a difference between running code in for, while, do...while loops or in timers. In loops code is immediately done and you can't see this, in timers you can. Even if you try to put in loops if counters, it will not works:
for(int i=a;i<b;++i)
{
if (cnt < 1000000)
{
IncrProgressBar();
cnt++;
}
else
{
cnt = 0;
}
}
If you want to use progressbar to do this then you must put in timer OnTick event code that adds data to database, and in this event increment progressbar value. It's similarly with changing form component's other properties (Text, Size, ...). If you want to see change on component you must use timers.
To change the value use:
private void timer1_Tick(object sender, EventArgs e)
{
progressBar2.Value = progressBar2.Value - 15;
}
In C#