ProgressBar in WPF Slows Down - c#

I have read any resource under the sun on how to implement a progress bar in wpf, that will actually reflect the actual progress of a thread.
Note - ** I am tied to .NET 4 only, due to corporate decisions, so any of the newer technics will not be at my disposal unfortunately.
This is what I have tried so far:
the method I run is LoadTable(). when run directly, it takes about 5 seconds to finish. when it's run in a BackgroundWorker it takes about 15 seconds (3 times longer than calling it directly):
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bg = (BackgroundWorker)sender;
for (int i = 0; i <= 100; i++)
{
// a method that takes time to exceute.
LoadTable();
bg.ReportProgress(i);
}
}
I also tried to run the method on its own thread and the BackgroundWorker that reports to the progress bar on another thread like so:
private void LoadTable_2()
{
task = Task.Factory.StartNew(new Action(() => LoadTable()));
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
LoadTable_2();
BackgroundWorker bg = (BackgroundWorker)sender;
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(i);
bg.ReportProgress(i);
}
}
But in this case, there is no connection between the two threads and there is no way of preventing the progress bar from running to 99, and waiting there until the other thread that executes the LoadTable method will finish. it doesn't report the actual progress of the method's execution.
My question is how do I use BackgroundWorker to actually report the progress without slowing the execution down? Thanks!

No wonder it's slower when you call LoadTable() a hundred times (!) in a loop. You should only call it once.
Your LoadTable() API apparently doesn't support reporting progress though. There is no way to report progress of a method that doesn't return the current progress.
You should consider showing an intermediate progress bar (set IsIndeterminate to true). This is the best you can do because you can't know the current progress of LoadTable() if it doesn't report it.

Related

Is it Possible to Make a Simple C# Marquee Progress Bar without a Background Worker?

I have a very simple form with a progress bar on it and a delete button. When the user clicks "Delete", a stored procedure runs for around five minutes.
I would like a simple marquee progress bar to run but it never appears. I read all the other questions about this but they all required a background worker.
Is it possible to make a marquee progress bar without a background worker?
public partial class ProgressBarSample
{
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
}
}
Your code
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
progressBar1.Visible = false;
prevents windows messages from being processed by your application until the delay completes because you block the thread responsible for processing those messages. Windows relies on those messages being processed in a timely manner.
The result is that your user interface will appear unresponsive to the user.
There are a number of mechanisms that allow you to do your processing on a second thread. BackgroundWorker is one that was commonly used at the time WinForms first came out and still a solid choice. You can use any technique that does the long-running work on another thread, but you must do it on that other thread.
You can use the async pattern to simplify the coding for that other thread
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
await Task.Run(() =>
{
// run stored procedure that takes around 5 minutes
Task.Delay(10000);
});
progressBar1.Visible = false;
}
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces your problem, it's impossible to say for sure what the issue is. However, the code you posted won't work. Your btnDelete_Click() method sets the Visible property to true, but then immediately sets it back to false, because the Task.Delay() method doesn't actually block.
Probably what you want is this:
private async void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
// run stored procedure that takes around 5 minutes
await Task.Delay(10000);
progressBar1.Visible = false;
}
BackgroundWorker is the generally accepted method for doing such background work, hence the name; but for a primitive "show for X period of time", you could use a Timer that checks the amount of time passed since (in this case) delete was last clicked to see if it should hide the control (and disable itself, no use ticking when there is nothing to do.)
Something like:
public partial class ProgressBarSample
{
TimeSpan pbShowDuration = [blah blah];
DateTime pbShowFrom = DateTime.MinDate;
public ProgressBarSample
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.Visible = false;
}
private void btnDelete_Click(object sender, EventArgs e)
{
progressBar1.Visible = true;
pbShowFrom = DateTime.Now;
timer1.Enabled = true;
// run stored procedure that takes around 5 minutes
}
private void timer1_Tick(object sender, EventArgs e)
{
if ((DateTime.Now - pbShowFrom) > pbShowDuration)
{
timer1.Enabled = false;
progressBar1.Visible = false;
}
}
}
But how are you planning to update the progress bar?
With the new task, sync, await features in C# you have a lot of options. If you don't need to do anything other than let the user know the operation is done you can start your progress bar, then start a task that runs your proc and when it's done stop the progress bar. I would personally put something other than a progress bar. To me a progress bar means you have a finite amount of time to wait. If your SP can vary in time I would go with some kind of busy display icon or something like that.
I personally would useh Task.ContinueWith in this case.
This MSDN article show a great way to handle it.
https://msdn.microsoft.com/en-us/library/dd270696(v=vs.110).aspx
Background worker is an old method which is superseded by using Tasks. Tasks have more functionality, can do things backgroundworkers cannot, and are much simpler to use.

Thread sleeping in a BackgroundWorker

I wrote a simple app that adds 100000 lines of "Hello World" to a list using a BackgroundWorker.
Below is the code of the work that my backgroundworker is doing in a separate thread:
private void BgWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
int min = 0;
foreach (var hw in hwList)
{
//new ManualResetEvent(false).WaitOne(1);
Thread.Sleep(1);
int progress = Convert.ToInt32((Double)min / hwList.Count * 100);
min++;
bgWorker.ReportProgress(progress);
}
}
// Updating the progress
private void BgWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
ProgressBar.Value = progressChangedEventArgs.ProgressPercentage;
}
All is working fine, except that if I remove the Thread.Sleep(1) the BackgroundWorker doesn't report the progress anymore. (I suppose it needs some time). Suspending the thread for 1 ms actually makes the BackgroundWorker report the progress but it's very slow.
My question is, is there a way I can get rid of thread sleeping but at the same time making the BackgroundWorker report the progress correctly?
From my understanding, suspending the BackgroundWorker is inevitable, since the thread needs some time to perform the task, but I'm wondering if there's a workaround.
I had issues where i was reporting progress to often, furthermore there is no reason to report the same progress so many times waste of cpu cycles.
private void BgWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
int min = 0;
int oldProgress = 0;
foreach (var hw in hwList)
{
// new ManualResetEvent(false).WaitOne(1);
// Thread.Sleep(1);
int progress = Convert.ToInt32((Double)min / hwList.Count * 100);
min++;
// Only report progress when it changes
if(progress != oldProgress){
bgWorker.ReportProgress(progress);
oldProgress = progress;
}
}
}
// Updating the progress
private void BgWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs)
{
ProgressBar.Value = progressChangedEventArgs.ProgressPercentage;
}
Instead of BackgroundWorker, I developed a class named SxProgress with a very simple interface that you may use in the following way :
int DesiredLinesCount = 100000 ;
List<string> Lines=new List<string>() ;
object[] UserObjects = new object[] { Lines } ;
SxProgress.Execute("Building lines",DesiredLinesCount,true,false,
BuildLines_ExecInThread,UserObjects)) ;
private bool BuildLines_ExecInThread(int ItemIndex,object[] UserObjects)
{
// some sleep to slow down the process (demonstration purpose only)
// if (ItemIndex % 10 ==0) System.Threading.Thread.Sleep(1) ;
List<string> Lines= (List<String>)UserObjects[0] ;
Lines.Add("Hello world") ;
return true ;
}
The SxProgress code is in the last message of this link
Note also that the class provides the same easy interface for parallel process, creating as many threads as cores in the computer with transparent dispatch of the items to the different threads.
will this work for WPF?
Unfortunately no : The class encompasses a form (winforms) with progressbar, labels and stop button.
Possible solution : I never tried it.
It may work by adding to WPF project 2 references from the "Add reference" dialog form (in ".NET" tab),i.e. "System.Windows.forms" and "WindowsFormsIntegration".
Then, in source code, add "using System.Windows.Forms;"
and "using System.Windows.Forms.Integration;"

How to use progressbar, backgroundworker, windows form together in C#?

I am stuck on an issue where I am using Backgroundworker to show the progress of my work in a progress bar. Code used for backgroundworker:-
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(200);
for (int i = 0; i <= 100; i++)
{
Delegate del= new DELEGATE(simulateHeavyWork);
this.Invoke(del);
backgroundWorker1.ReportProgress(i);
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
backgroundWorker1.ReportProgress(0);
return;
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
percentLabel.Text = e.ProgressPercentage.ToString() + "%";
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Cancelled");
}
else
{
MessageBox.Show("Completed");
}
}
I have created a delegate on the code:-
public partial class Form1 : Form
{
private delegate void DELEGATE();
public Form1()
{
InitializeComponent();
}
private void simulateHeavyWork()
{
Thread.Sleep(100);
...lines of code to perform some search logs operation..
....
}
}
The functionality I want to achieve is that progress bar should report the progress of my function simulateHeavyWork() which is actually using UI thread as it needs to take input from my form controls and update it as well.
Now the problem which is happening is that code is actually calling simulateHeavyWork() and gives the output that is updating ui controls and work is done. (Note: I have used delegate here to avoid error cross controls running on ui thread as my function needs to use UI controls.)
Once that is done, it starts updating progress bar and which is wrong and looks like it calls simulateHeavyWork again and again with the gap of sleep(100).
user3222101, as Andy stated before, you are running simulateHeavyWork() continuously. Moreover, by calling Invoke you are running this method in the UI thread which cause an extra sleep in the UI thread. Basically Invoke uses the message loop (pump) of the Control you use it on (Form1 in that case) and put your delegate to the queue of the UI thread in order to execute. This is not a good practice I think, due to the Sleep() call and time consuming log operations in your simulateHeavyWork() method.
I hope, understand you problem clearly.What I suggest is separation of the time consuming log operations from UI thread. Do not spend the valuable time of UI thread with slow and boring I/O operations. Get the values from the controls (using Invoke in the BackgroundWorker as I will explain below), do whatever you want in BackgroundWorker and update your GUI (again using Invoke) without touching the UI thread for this kind of heavy tasks.
As Andy suggested, you can pass data via the parameter of RunWorkerAsync and you should create a class which can store any data you need (because it accepts only one parameter). However, you can get the values from your Form whenever you need from another thread by using Invoke. Invoke
method also returns the value from your delegate (please see the example at the link below) and this gives you a chance to get the values of your controls on the form. Create a delegate which returns an object of type class that you crated for RunWorkerAsync and use this values in the BackgroundWorker thread. Please, have a look at the example in here.
public static string GetTextThreadSafe(this TextBox box)
{
return GetTextBoxText(box);
}
Also, example uses Func<...> in order to return value.
By this way you can sleep (in BackgroundWorker thread) for a while then get the values from your controls (current values) and do whatever you want (again in BackgroundWorker thread). I think, this improves your code.
From your question: "which is wrong and looks like it calls simulateHeavyWork again and again with the gap of sleep(100)."
Of course it calls. Just look at your code:
for (int i = 0; i <= 100; i++)
{
Delegate del= new DELEGATE(simulateHeavyWork);
this.Invoke(del);
So you are calling simulateHeavyWork 100 times here. And since you've typed Thread.Sleep(100); in the body of simulateHeavyWork - gap between calls is about Sleep(100)

WPF interface is updated only after the DispatcherTimer thread finishes

I have a WPF application and I am working with the .NET Framework 4.0 and C#. My application consists of an interface with several controls. In particular I have a task that needs to be executed periodically every 10 seconds. In order to execute it I use a System.Windows.Threading.DispatcherTimer. The ViewModel looks like this:
public class WindowViewModel {
protected DispatcherTimer cycle;
public WindowViewModel() {
this.cycle = new DispatcherTimer(DispatcherPriority.Normal,
System.Windows.Application.Current.Dispatcher);
this.cycle.Interval = new TimeSpan(0,0,0,0,10000);
this.cycle.Tick += delegate(object sender, EventArgs e) {
for (int i = 0; i < 20; i++) {
// Doing something
}
};
this.cycle.Start;
}
}
As I said the routine called periodically does something. In particular there is some heavy logic there which causes that routine to take some seconds to execute and complete. Well it is a different thread so I should be ok and the interface is not supposed to freeze.
The problem is that that routine causes the viewmodel to be updated. Several data are updated, and the corresponding View is bound to those data. What happens is that all updated data are refreshed once at a time when the routine completes. I want data to be updated during the thread execution.
In particular inside that routine I have a for cycle. Well at the exit of the cycle everything is updated in the interface. How to achieve this? Where am i doing wrong?
The DispatcherTimer uses the supplied Dispatcher to run the timer callback.
If you take a look at the docs for Dispatcher, there's a clue:
Provides services for managing the queue of work items for a thread.
So, by using the System.Windows.Application.Current.Dispatcher, you're using the Dispatcher that manages "the queue of work items" for the UI thread.
To run your work in the ThreadPool instead, you could either use System.Threading.Timer or use ThreadPool.QueueUserWorkItem in your DispatcherTimer callback.
If you combine this with the following extension method, it becomes easy to marshal any UI specific stuff back to the Dispatcher when you finish your heavy workload:
public static class DispatcherEx
{
public static void InvokeOrExecute(this Dispatcher dispatcher, Action action)
{
if (dispatcher.CheckAccess())
{
action();
}
else
{
dispatcher.BeginInvoke(DispatcherPriority.Normal,
action);
}
}
}
then...
this.cycle.Tick += delegate(object sender, EventArgs e) {
ThreadPool.QueueUserWorkItem(_ => {
for (int i = 0; i < 20; i++) {
// Doing something heavy
System.Windows.Application.Current.Dispatcher.InvokeOrExecute(() => {
//update the UI on the UI thread.
});
}
});
};

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.

Categories

Resources