Label not updating in statusstrip with progressbar - c#

I have been developing a windows forms application in vc# express 2013. I have a statusstrip control where i have a label and a progress bar. I want to show the progress of progressbar in label but it is not updating with the progress bar value. it only gets updated when the progress bar is complete i.e. when value is 100 and the job is finished. The strange thing is that the progress bar is updated but the label is not... Any ideas what might be happening here? By the way, I raise a progress event in my own code which is handled and the progress is updated accordingly...
Thanks
I raise the event ProgressTick in MGFDataHandler Class
if ((hundredthValue*progressMultiplier == lineCounter) || (lineCounter==totalLines))
{
//Raise the progress bar event!
EventHandler handler = ProgressTick;
if (handler != null)
{
handler(this, e);
}
progressMultiplier++;
}
I then handle it in windowsform as follows:
void msDataHandler_ProgressTick(object sender, EventArgs e)
{
var progress = (int)((MGFDataHandler)sender).progressMultiplier;
if (progress < 100)
{
this.progressStatusString.Text = "Progress: " + progress + "%";
this.progressBar.Visible = true;
this.progressBar.Value = progress;
}
else
{
this.progressStatusString.Text = "Done";
this.filterMSDataToolStripMenuItem.Enabled = true;
}
}
The function which starts the whole thing is in the windowsform_activated event and MGFDataHandler object is an attribute of the windowsform.
private void GlycoMSPipelineMainFrame_Activated(object sender, EventArgs e)
{
switch (this.whoActivatedMe)
{
case WhoActivatedMe.MSFilterWizard:
{
this.whoActivatedMe = WhoActivatedMe.Me;
foreach (String currentmgffilename in this.currentMGFFileNames)
{
mgfDataHandler.Filter_MS_Data(currentmgffilename);
}
this.populateTreeViews();
break;
}
default:
{
break;
}
}
}
It is the Filter_MS_Data that raises the event.

Try using a background worker (found in System.componentModel) to run the task that you are performing. When updating the label or progress back you will need to check if invoke is required.
Something like this.
If (label.InvokeRequired) {
label.Invoke(new MethodInvoker(delegate
{
label.Text = progress.Value;
});
}
Invoke is required to prevent cross threading operations which in turn can produce race conditions.
Forgive the formatting i typed this out on my phone.
You will need to perform the population using a background worker. I think this will resolve your issue.
Edit:
The background worker provides a do work event which will run on another thread. It also provides a finished event so you know when the task is complete.
I think what is happening here is that your program is performing work on the ui thread which prevents the ui from fully updating.

I do need some more information for a full answer, but the direction in which this is heading to is quite clear to me.
The problem is that you're probably doing everything in the same thread. When you have a big job you need to use a background thread to do the work, and let the main thread (i.e. a UI thread) handle the UI.
This is a big topic and I'm just scratching the surface here, so first of all read the following links and try to create a thread for your job and invoke the update of the progress bar yourself. If not successful, then post a new question with more information.
The links you should read are:
Threads in C#
ProgressBar in C#

I realize this is a very old question, but i found it with the same issue in VS2019. I found that rather than adding a thread, you can call the Refresh method of the container of the label and ProgressBar.
void msDataHandler_ProgressTick(object sender, EventArgs e)
{
var progress = (int)((MGFDataHandler)sender).progressMultiplier;
if (progress < 100)
{
this.progressStatusString.Text = "Progress: " + progress + "%";
this.progressBar.Visible = true;
this.progressBar.Value = progress;
}
else
{
this.progressStatusString.Text = "Done";
this.filterMSDataToolStripMenuItem.Enabled = true;
}
this.progressStatusString.Owner.Refresh();
}

Related

C# Properly raise event from background thread and handle it in the UI thread

I have been looking all over the web at examples like this:
https://msdn.microsoft.com/en-us/library/edzehd2t%28v=vs.110%29.aspx
and all over stackoverflow but I cannot get this to work or find the answer. Or the answer doesn't apply to what I am doing, or there is not enough information in the answer for me to understand how to make this work.
I'm trying to write good code here and avoid Application.DoEvents(). Take a look at the code.
private void btnSendQ_Click(object sender, EventArgs e)
{
//launch a thread from a button click event
Thread ManualQueryThread = new Thread(StartManualQueryThread_Thread);
ManualQueryThread.Priority = ThreadPriority.Normal;
ManualQueryThread.IsBackground = true;
Platform.Log(LogLevel.Debug, "Starting Manual Query Thread.");
Stats.g_CurrentStatus = "Starting Manual Query Thread.";
ManualQueryThread.Start();
}
private void StartManualQueryThread_Thread()
{
try
{
//Here work is performed in the background thread populating objects with data.
}
catch (Exception e)
{
Platform.Log(LogLevel.Error, "Error in StartManualQueryThread(). The error is: " + e.ToString());
}
finally
{
//raise event here since the thread has completed
OnC_FindComplete(EventArgs.Empty);
}
}
public event EventHandler C_FindComplete;
protected virtual void OnC_FindComplete(EventArgs e)
{
//display the gathered information in a datagrid using DisplayC_FindResults(), and enable UI objects.
EventHandler handler = C_FindComplete;
DisplayC_FindResults();
btnSendQ.Enabled = true;
btnMove.Enabled = true;
}
The problem is when a release build is created the datagrid gets a null reference exception and does the red X thing because it is still being updated by a background thread. So how do I properly raise an event in a background thread, and process that event in the UI thread to interact with the UI?
Try to use BackgroundWorker. You'll be able to update your UI in RunWorkerCompleted event.

TextBlock Will Not Update

I have a text block called "findListText". Here, I am updating the text in it:
private void InstantSearch(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
HitEnter = true;
}
findListText.Text = "Processing request. Please wait...";
Find(bool.Parse("False" as string));
}
However, the next set of code is a search function that can take up to 10 seconds, and at the end of it, it changes the text in findListText again.
private void Find(bool? bForward = true)
{
{
//Lots and lots of code
}
findListText.Text = "Search completed."
}
The problem is, the textblock never seems to update to "Processing request. Please wait...". The textblock is in it's original state and 10 seconds later updates to "Search completed.", seemingly skipping out the middle man.
I'm using C# - WPF. What am I doing wrong here?
Doesn't matter what technology I think.
The code is running on the same thread, meaning the the UI won't be updated untill all the code on that thread is completed. You should address a different thread to update that textblock.
In that case, you will have 2 thread:
The origininal thread, executing the "lots and lots of code"
The second (extra) created thread, which will handle updating the textblock's text while the other thread is executing the other code.
I've created a little something that should resolve your problem, it's based on this Stack Overflow page
Since this is WPF, try the following: after changing the text to "Processgin", call:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { this.UpdateLayout(); }));
This will tell the thread to update the UI as soon as possible.
Here is how to run your find method in its own thread.
private void InstantSearch(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
HitEnter = true;
}
findListText.Text = "Processing request. Please wait...";
BackgroundWorker tempWorker = new BackgroundWorker();
tempWorker.DoWork += delegate
{
Find(bool.Parse("False" as string));
};
tempWorker.RunWorkerAsync();
}
If you try that, you'll get an error because you access your UI thread from the background thread. So you'll need to update your find method as well.
private void Find(bool? bForward = true)
{
{
//Lots and lots of code
}
Dispatcher.BeginInvoke((Action) delegate {
findListText.Text = "Search completed."
});
}
You should look into the UI threading concept of WPF. Invoke the Dispatcher to modify the textbox. Also the search should run with ThreadPool.QueueWorkerItem.
// Start worker thread
ThreadPool.QueueUserWorkItem(state =>
{
// Long running logic here
findListText.Dispatcher.BeginInvoke(() => findListText.Text = "Processing request. Please wait...");
Find(bool.Parse("False" as string));
// Tip: Run change on GUI thread from the worker using the dispatcher
findListText.Dispatcher.BeginInvoke(() => findListText.Text = "Search completed.");
});
I came back to this just now, and had another browse across the internet for similar problems. For something as simple as pushing a single message before a long running process occurs, I'm surprised no-one suggested "Application.DoEvents();". Yes, I know it has it's flaws, and my UI will still hang, but this suits me perfectly for my situation, at least.

Program doesn't increment progress bar while waiting for thread to exit

I have a c# form app that serves as an UI and executes an external exe. I want to make a progress bar increment until the external exe finishes executing. so i have the following code:
// create thread and Start external process
Thread MyNewThread = new Thread(new ThreadStart(startmodule));
MyNewThread.Start();
do
{
if (progressBar1.Value < 100)
{
progressBar1.Value++;
}
} while (MyNewThread.IsAlive);
label5.Text = "Status: Done";
// startmodule()
void startmodule()
{
ProcessObj = new Process();
ProcessObj.StartInfo.FileName = ApplicationPath;
ProcessObj.StartInfo.Arguments = ApplicationArguments;
ProcessObj.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
ProcessObj.Start();
}
Instead it fills the bar up instantly and shows "Done" message but the external exe (AppPath) still runs in the background.
Please post some ideas im stuck. i don't know whats wrong. Thank you for your time.
You cannot make this work, you cannot guess how long the process will take. Set the ProgressBar.Style property to Marquee. Set it Visible property to true when you start the process. Use the Process.Exited event to set it back to false. Like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void ButtonRunProcess_Click(object sender, EventArgs e) {
var ProcessObj = new Process();
ProcessObj.SynchronizingObject = this;
ProcessObj.EnableRaisingEvents = true;
ProcessObj.Exited += new EventHandler(ProcessObj_Exited);
ProcessObj.StartInfo.FileName = #"c:\windows\notepad.exe";
// etc...
ProcessObj.Start();
progressBar1.Visible = true;
}
void ProcessObj_Exited(object sender, EventArgs e) {
progressBar1.Visible = false;
}
}
Well the loop is being run so fast, that it reaches 100% before your task is actually completed. The condition that the loop is being check for (The thread being alive) is going to be true until your task is completed, but the loop is causing the progress bar to fill up prematurely.
In order to run a progress bar you have to be able to quantify the progress of the long running task. You have nothing in the code that attempts to quantify this.
You would need there to be communication between the two processes in order to make this progress bar work well. In other words the external process needs to send messages back to the parent app informing the parent app of the measure of progress. Now, that can be hard to achieve so a marquee style progress bar may be more appropriate.
Finally i got some "free" time to test the backgroundworker as suggested above. i can say it's the best solution and it doesn't freeze the UI. Example implementation follows:
preparemodule()
{
ProcessObj = new Process();
ProcessObj.StartInfo.FileName = ApplicationPath;
ProcessObj.StartInfo.Arguments = ApplicationArguments;
}
void run_Click(object sender, EventArgs e)
{
preparemodule();
backgroundWorker1.RunWorkerAsync(ProcessObj);
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int i=0;
ProcessObj.Start();
while (checkifexists("notepad", 0) == true)
{
i++;
label5.Text = "Status: notepad running... " + progressBar1.Value.ToString() + "%";
Thread.Sleep(3000);
backgroundWorker1.ReportProgress(i);
if ((backgroundWorker1.CancellationPending == true))
{
e.Cancel = true;
}
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage <= 100)
{
progressBar1.Value = e.ProgressPercentage;
}
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label5.Text = "Status: Done";
}
void cancel_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
As you see we can even cancel it. and by checking if notepad is running we can increment out progressbar. Dont forget to enable bgWorker's "reportsprogress" and "supportscancellation" properties somewhere in your code. i hope it helps someone.
First, #Tim answer is right about what is happening.
If you can control the external app, make a way to it communicate with the main process telling the current state and update the progress bar according to these messages.
If is not possible, try to estimate the execution time and set the progress according to the execution time. This is valid if it performs always in same time for the same task.
Background worker thread was designed for this sort of thing.
It has an event you can fire while processing something, you handle it and update your progress bar. Course as noted by others you don't seem to have any measure of progress, just some time has passed, so it's not really an indication of progress you want but some sort of "I'm busy" animation, if you use a progress bar for that you get all sorts of issues that drive the UI boys mad, like it never gets to 100%, or it gets to 100% well before the operation has finished, or even cycles round.
So if you can indicate some progress from the thread, e.g if you are looping through X items fire the progress event every 10% of X. Use a Background worker thread.
If you can't don't use a progress bar kick the thread off an make some animated control visible. When the thread finishes make the animation invisible again. What and how of the animation is up to you and your UI boys.

have a progress bar in winform to show the time taken for the encryption of file c#

Currently my application is doing encryption of files and folder and i trying to have a progress bar to make the application a nicer interface and also to have know how long it take to encrypt.
However this is my first time using progress bar and i kinda confuse with all the terms mention such as background worker,steps,maximum etc. was wondering anyone know how to create and set a simple version of progress bar. thanks in advance .
AFTER see-ing swordfish suggestion and trying it out..here the code i have
this is the part of my code based on the link provided and i tried it
and the part where i used part of the code to the button
public LockPasswordBox(IFile[] info)
{
InitializeComponent();
ifile = info;
// To report progress from the background worker we need to set this property
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
// On worker thread so do our thing!
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Your background task goes here
for (int i = 0; i <= 100; i++)
{
// Report progress to 'UI' thread
backgroundWorker1.ReportProgress(i);
// Simulate long task
System.Threading.Thread.Sleep(100);
}
}
// 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;
}
button code
private void lockButton_Click(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
Problem facing is..after i press the button.it just straight pop out the message box encryption success.
If you have Minimun = 1, Maximum = 100 and Step = 1 you need to call PerformStep() 99 times for the progressbar to complete.
If you can not get accurate information from lControl.Encrypt(details) about how far the operation is gone you can not know when to update all those steps on your progressbar.
The MSDN Documentation has a simple example on how to get the progressbar moving.
i had a similar requirement and this helped me with my task. Hope it will help you too.
http://www.codeproject.com/Tips/83317/BackgroundWorker-and-ProgressBar-demo
Its quiet simple actually all you have to do is report the progress from the background worker using the not so mysterious reportprogress method and have a method to update the progress bar when ever the progress is reported.
Try to implement it this way and if you hit a road block post your code.

c# backgroundWorker not raising ProgressChanged or RunWorkerCompleted events

Using C# .NET 3.5.
It seems I'm not the only one, since a search seems to bring up a lot of similar questions, however reading through them I seem still to be missing the solution.
I'm raising an event with a button click on my UI, which is then supposed to start a backgroundWorker to get some time consuming work done - in this case, I want it to collect information from a form and a) write to an xml file as well as b) insert the information into a remote database.
This is exactly what the backgroundWorker was designed and intended to do I believe.
Here is the event raising code.
private void btnFinish_Click(object sender, EventArgs e)
{
clkFinish.setInstant();
curAct.setTimeFinish(DateTime.Now);
btnStart.Enabled = true;
bgwDataWorker.RunWorkerAsync();
for (int i = 0; i < 20; i++)
{
Console.WriteLine("Thread a: " + i);
Thread.Sleep(100);
if (i == (20 - 1))
{
Console.WriteLine("Finished");
}
}
}
As you can see, I have some code there which I've used as a counterbalance to the background worker code, which is here:
private void bgwDataWorker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Running in a different thread now");
int count = 0;
for (int i = 0; i < 21; i++)
{
Console.WriteLine("Thread b: " + i);
Thread.Sleep(100);
(sender as BackgroundWorker).ReportProgress(5 * i, null);
if (i == (21 - 1))
{
count = i;
}
}
e.Result = count;
}
So far, everything seems to be working up to this point.
My problem is that when the code in the DoWork method is finished, nothing is happening. Here are the ProgressChanged and RunWorkerComplete methods.
private void bgwDataWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine(e.ProgressPercentage);
}
private void bgwDataWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("Result is " + e.Result.ToString());
}
This has me baffled.
I've tried running the examples from MSDN and I'm experiencing the same problems. RunWorkerCompleted and ReportProgress events are simply not being raised for some reason and I don't understand why.
Thanks for reading.
Alan
I had the same problem and I just figured out the answer.
In the properties of the backgroundworker, make sure that ProgressChanged and RunWorkerCompleted list the name of their functions (mine are called 'backgroundWorker1_ProgressChanged' and 'backgroundWorker1_RunWorkerCompleted' respectively).
You didn´t miss to set these variables, did you?
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
Also it could be an exception in the DoWork-Method, that isn´t handled.
Inksmithy, you mentioned you have renamed the BackgroundWorker. Please note that when you do this, the Forms Designer does NOT automatically change the reference to the old DoWork and RunWorkerCompleted methods. So in your Forms designer, click on your BackgroundWorker, then click on the Events icon (lightning bolt) in the Properties Window, and double-check that it is pointing to the correct Event Method Names.
Set WorkerReportsProgress property to True.
You've left out a vital bit - a boolean flag to allow it to raise the events
bgwDataWorker.WorkerReportsProgress = true;
bgwDataWorker.WorkerSupportsCancellation = true;
I had one project [A], (built a while back) that had the progress bar working. The current app [B] is not reporting progress. For some reason app [A] had the progress and work completed event handlers wired up. So, I hooked up the event handlers in app [B] and it is working. The handlers are copied into the designer.cs partial class file. (Assuming they could go in the form.cs partial class).
//
// backgroundWorker1
//
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
//
hope this helps. Don't know why the app [A] was wired up by using the toolbox to drop the background worker on the form design window. Using VS2012 and Windows 7.
Also, I am calculating the progress percentage and for some reason the number of items in the listbox times the number of selected checkboxes exceeded 100% - it threw and error. So, I will cap the percentage at 99 and make that part of my logic for the progress percentage being reported back to the event.
I had this issue recently. It was caused because I had the main thread sleeping until the background thread was finished, which blocked the update from executing.
I solved this by wrapping my object (containing worker threads) into another thread so the progress update could execute, change the values in the main thread allowing my sleeping thread to continue.

Categories

Resources