Making a progress bar update in real time in wpf - c#

I'm having some trouble making the progress bar show the updates in real time.
This is my code right now
for (int i = 0; i < 100; i++)
{
progressbar1.Value = i;
Thread.Sleep(100);
}
But for some reason the progress bar shows empty when the function runs, and then nothing until the function finishes running. Can someone explain to me how this can be done? I'm new to C#/WPF so I'm not 100% sure on how I would implement a Dispatcher on a different thread (as seen on some other posts) to fix this problem.
To clarify, my program has a button which when press, grabs the value from a textbox, and uses an API to retrieve info, and create labels based on it. I want the progress bar to update after every row of data is finished processing.
This is what I have right now:
private async void search(object sender, RoutedEventArgs e)
{
var progress = new Progress<int>(value => progressbar1.Value = value);
await Task.Run(() =>
{
this.Dispatcher.Invoke((Action)(() =>
{
some pre-processing before the actual for loop occur
for (int i = 0; i < numberofRows; i++)
{
label creation + adding
((IProgress<int>)progress).Report(i);
}
}));
});
}
Thank you!

If you are using .NET 4.5 or later, you can use async/await:
var progress = new Progress<int>(value => progressBar.Value = value);
await Task.Run(() =>
{
for (int i = 0; i < 100; i++)
{
((IProgress<int>)progress).Report(i);
Thread.Sleep(100);
}
});
You need to mark your method with async keyword to be able to use await, for example:
private async void Button_Click(object sender, RoutedEventArgs e)

Managed to make it work. All I needed to do is instead of making it just
progressBar1.value = i;
I just had to do
progressbar1.Dispatcher.Invoke(() => progressbar1.Value = i, DispatcherPriority.Background);

You should use BackgroundWorker included in .NET, which provides you with methods for reporting the progress of a background thread in an event. The thread which created the BackGroundWorker automatically calls this event.
The BackgroundWorker.ProgressChanged can be used to report the progress of an asynchronous operation to the user.
// This event handler updates the progress bar.
private void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Refer to MSDN for more information about using this.

Related

Dispatcher.BeginInvoke() not running asynchronously

Here's a simplified version of what I want to do:
onClick a button, a aNewMethod() would run asynchronously to keep UI responsive. That's it!
I've read some answers and here's what i could come up with :
private async void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod());
await task;
}
private void aNewMethod()
{
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
}
As you may have thought, this throws a System.InvalidOperationException at if(progress.Value== 0) saying :
The calling thread cannot access this object because a different
thread owns it.
after some Googling, I've read that I need a Dispatcher.BeginInvoke() method to update/use UI controls, so I did this :
private void aNewMethod()
{
Dispatcher.BeginInvoke((Action)delegate {
if (progress.Value == 0)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Value = 100;
}
});
}
This solved the System.InvalidOperationException but it's not running asynchronously as the UI still freezes at for loop
So the question is : How to run the aNewMethod(); asynchronously and still update and interact with UI controls ?
The Dispatcher runs in the UI thread. It handles your UI, and executes actions you pass to it via BeginInvoke etc.
But it can only handle one thing at a time; when it's busy handling your action, it won't update the UI in the meantime, so the UI freezes in the meantime.
If you want to keep your UI responsive, you'd need to run the heavy load functions in a seperate, non-UI thread. Within those functions running on another thread, you can call the dispatcher whenever you need access to the UI - ideally, only very briefly for the purpose of updating UI elements.
So in other words, you'd want to be running your sleep function in a seperate thread, and then just make a call to the Dispatcher from your own thread when you need to set the progress value. Something like
private void button_Click(object sender, RoutedEventArgs e)
{
Task task = Task.Run(() => aNewMethod()); // will call aNewMethod on the thread pool
}
private void aNewMethod()
{
double progressValue = 0;
Dispatcher.Invoke(() => progressValue = progress.Value);
if (progressValue == 0)
{
Thread.Sleep(3000); // still executes on the threadpool (see above), so not blocking UI
Dispatcher.Invoke(() => progress.Value = 100 ); // call the dispatcher to access UI
}
}
With you current implementation, there is no need to use Thread.Start, as in that method you are just sleeping it for some time and accessing UI thread objects there which is not allowed
.In your scenario a better way of doing is that you should not use Thread.Sleep instead of that you should be doing it with Task.Delay like:
private async void button_Click(object sender, RoutedEventArgs e)
{
if (progress.Value == 0)
{
await Task.Delay(3000);
progress.Value = 100;
}
}
Now you don't need Dispatcher.Invoke, and credit goes to async and await keywords as statements after await will be executing in the same calling synchronization context from where we did async call which is UI thread in this case.
onClick a button, a aNewMethod() would run asynchronously to keep UI responsive.
Actually, it's running synchronously on a background thread. Task.Run is perfectly appropriate for this.
after some Googling, I've read that I need a Dispatcher.BeginInvoke() method to update/use UI controls
Unfortunately, this is one area where Google will certainly mislead you. Dispatcher is not the best solution here.
Instead, you should use IProgress<T>/Progress<T>, as such:
private async void button_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<int>(value => { this.progress.Value = value; });
await Task.Run(() => aNewMethod(progress));
}
private void aNewMethod(IProgress<int> progress)
{
//Heavy work
for (int i = 1; i < 1000000000; i++) { }
progress.Report(100);
}

Label not visible on Panel when calling visible = true

I already tried google to find an answer to my problem but haven't found a solution.
I working with C# and WinForms. I created a panel and added a label to it. This panel is set to myPanel.Visible = falseat first. I want to set it myPanel.Visible = true when I click a button. The button is calling a function. During the function call I want to show a progessbar in the panel, so I set this myPanel.Visible = trueand at the end of the function I set it back to myPanel.Visible = false.
The problem is, that the label isn't visible.
When I don't set myPanel.Visible = false at the end of the function, the label is visible, but only at the end of the function.
I also tried to programmatically add the label in the function called, still not working. The second idea I tried was to use this.PerformLayout(); during the call of the function.
It seems like that the application is drawing the label only at the end of the function call, but I need it to be drawn during the function is called.
Thanks for any help.
private void buttonAdd_Click(object sender, EventArgs e)
{
//Adding label to panel
MyLabel label = new MyLabel();
label.Text = "Test";
label.Location = new Point(0, 0);
progressPanel.Controls.Add(label);
//Showing progressPanel
progressPanel.Visible = true;
progressBar1.Minimum = 1;
progressBar1.Value = 1;
progressBar1.Step = 1;
//Some Code
progressPanel.Visible = false;
}
Your problem is obviously that you are performing your task in the UI thread. So the UI itself doesn't get repainted while this task is working. And when you finished, it's still invisible.
Try using a BackgroundWorker instead. Use its ProgressChanged event to update your progress bar.
If you show some code, I can go into details of implementation.
UPDATE:
I actually prefer sstan's answer, but I promised to show the BackgroundWorker way. It may still be helpful if for some reason you cannot use async/await:
public partial class Form1 : Form
{
private BackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker.DoWork += DoWork;
_backgroundWorker.RunWorkerCompleted += WorkerCompleted;
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.ProgressChanged += WorkerProgressed;
}
private void buttonAdd_Click(object sender, EventArgs e)
{
//Adding label to panel
MyLabel label = new MyLabel();
label.Text = "Test";
label.Location = new Point(0, 0);
progressPanel.Controls.Add(label);
//Showing progressPanel
progressPanel.Visible = true;
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
progressBar1.Value = 0;
progressBar1.Step = 1;
// to avoid multiple starts
buttonAdd.Enabled = false;
// start working
_backgroundWorker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
_backgroundWorker.ReportProgress(1);
// work
_backgroundWorker.ReportProgress(50);
// more work
_backgroundWorker.ReportProgress(100);
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressPanel.Visible = false;
buttonAdd.Enabled = true;
}
private void WorkerProgress(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
}
So as you see, BackgroundWorkers are fairly easy to use, but async/await is even easier since you don't have to write so much code just for parallelization.
The behavior you see is normal. The changes in visibility only take effect when the UI thread gets a chance to repaint your window. But, while you function is running, the UI thread is busy, so it can't repaint your window. The UI thread only frees up at the end of your function call, so that's when the component's visibility changes take effect.
What you need to do is to perform your function's work on a different thread so that the UI thread is free to repaint your window while the function is still running.
One way to do this, is by using Task.Run() combined with the async/await keywords. Here is a basic example of how this could look like, using the code you posted:
async private void buttonAdd_Click(object sender, EventArgs e)
{
// ....
//Showing progressPanel
progressPanel.Visible = true;
progressBar1.Minimum = 1;
progressBar1.Value = 1;
progressBar1.Step = 1;
// this work will happen on separate thread,
// so the UI thread will be free to update the panel visibility
Progress<int> progress = new Progress<int>(percentage => progressBar1.Value = percentage);
await Task.Run(() => this.WorkToBePerformedOnSeparateThread(progress));
progressPanel.Visible = false;
}
private void WorkToBePerformedOnSeparateThread(IProgress<int> progress)
{
// do work...
progress.Report(25); // Report 25% completed...
// do more work
progress.Report(50); // Report 50% completed...
// more work
progress.Report(75); // Report 75% completed...
// etc...
}
As pointed out by Rene in the comments, just remember that you can only do UI work on the UI thread. So, in the example above, you'll notice that the progress reporting is performed through the Progress<T> class which allows you to change the progress bar value (UI work) from the separate thread, because it takes care of ensuring that the progress reporting happens on the UI thread.

Update UI control in Task

Does it right code, if I update my UI controls in task like this?
Or it's wrong, and I need use smth like Control.Invoke?
private async void button1_Click(object sender, EventArgs e)
{
textBox1.Text = await Task<string>.Factory.StartNew(() =>
{
Foo();
return "Completed";
});
}
private void Foo()
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
Thread.Sleep(1000);
}
}
The BCL addresses this specific scenario with the IProgress interface, implemented in the Progress class, to provide rich asynchronous progress reporting. This is available in .NET 4.5, or .NET 4 with the BCL Portability Nuget package. A lot of BCL classes accept an IProgress parameter for progress reporting.
Servy's answer addresses the immediate problem of how to update the UI after an asynchronous operation, but that forces you to mix UI code inside the long running operation. IProgress allows you to make an OnReport call with your report data without any concerns about marshalling the call to the proper thread, synchronization contexts, UI specific calls etc.
Your code can be as simple as this:
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<string>(msg=>textBox1.Text = msg);
await Task<string>.Factory.StartNew(() => Foo(progress));
}
private void Foo(IProgress<string> progress)
{
for (int i = 0; i < 100; i++)
{
progress.OnReport(i.ToString());
Thread.Sleep(1000);
}
progress.OnReport("Finished");
}
Or you can use a more complex progress type, eg
class MyProgressData
{
public string Message{get;set;}
public int Iteration {get;set;}
public MyProgressData(string message,int iteration) ...
}
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<MyProgressData>(msg=>{
textBox1.Text = msg.Message;
textBox2.Text=msg.Iteration.ToString();
});
await Task<string>.Factory.StartNew(() => Foo(progress));
}
private void Foo(IProgress<MyProgressData> progress)
{
for (int i = 0; i < 100; i++)
{
progress.OnReport(new MyProgressData("Hi",i));
Thread.Sleep(1000);
}
progress.OnReport("Finished");
}
The beauty of this is that you can completely decouple processing from reporting. You can put the processing code in a completely different class or even project from your UI.
You can just run the code to see that it won't work, and that your program will crash, as you are touching the UI from a non-UI thread.
You should instead update the UI as a continuation to the task that is scheduled to run in the UI thread. await does all of this for you, making the code pretty trivial.
You also shouldn't create a thread pool thread just to have it sit there and do nothing while you sleep for a fixed amount of time. Use Task.Delay instead.
private async void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(1000);
}
textBox1.Text = "Completed";
}
Your Foo method will throw, since you're trying to update a UI control from a background thread.
If Foo did something arbitrary, which doesn't evolve any UI controls, then your textBox1.Text = await Task.Factory.StartNew would be fine, since you use await and the SynchronizationContext would be implicitly captured and used once the await completes, which would make the assignment on the UI thread.
This doesn't make too much sense, but if you wanted to update a control from inside the Task, you'd have to use Control.Invoke.

C# Threading using invoke, freezing the form

I'm trying to use threads and prevent the program from freezing while the thread is busy. It should show the progress (writing of 0's / 1's) and not just show the result after its done, freezing the form in the meanwhile.
In the current program I'm trying to write to a textbox, and actually see constant progress, and the form can't be affected by the tasks of the other thread.
What I have now is I can write to a textbox with a thread using invoke, but It only shows the result (Form freezes while thread is busy), and the form freezes.
Form image:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace MultiThreading
{
public partial class MultiThreading : Form
{
public MultiThreading()
{
InitializeComponent();
}
Thread writeOne, writeTwo;
private void writeText(TextBox textBox, string text)
{
if (textBox.InvokeRequired)
{
textBox.BeginInvoke((MethodInvoker)delegate()
{
for (int i = 0; i < 500; i++)
{
textBox.Text += text;
}
});
}
else
{
for (int i = 0; i < 500; i++)
{
textBox.Text += text;
}
}
}
private void btnWrite1_Click(object sender, EventArgs e)
{
writeOne = new Thread(() => writeText(txtOutput1, "0"));
writeOne.Start();
}
private void btnWrite2_Click(object sender, EventArgs e)
{
writeTwo = new Thread(() => writeText(txtOutput2, "1"));
writeTwo.Start();
}
private void btnClear1_Click(object sender, EventArgs e)
{
txtOutput1.Clear();
}
private void btnClear2_Click(object sender, EventArgs e)
{
txtOutput2.Clear();
}
private void btnWriteBoth_Click(object sender, EventArgs e)
{
writeOne = new Thread(() => writeText(txtOutput1, "0"));
writeTwo = new Thread(() => writeText(txtOutput2, "1"));
writeOne.Start();
writeTwo.Start();
}
private void btnClearBoth_Click(object sender, EventArgs e)
{
txtOutput1.Clear();
txtOutput2.Clear();
}
}
}
EDIT:
Btw for anyone wondering, I'm new to multithreading and I'm just trying to write a small program to understand the best way to do this.
I understand that my previous invoke didn't realy help because I still wasn't giving the form a chance to update, so its getting there.
Ok so running 1 thread like this works, but still running multiple threads together, won't update the form till after the thread is done.
I've added a thread.sleep() so I can try and clear while writing, to see if I can still use the form.
When writing to 1 textbox I can still clear the screen while writing.
But once I use 2 threads, I can't use the form anymore till the thread completes, and gives the output.
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 500; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
Thread.Sleep(2);
}));
}
}
(If I'm totally wrong on this I don't mind having to read through some examples/threads, I'm still trying to see what is the best way to do this, besides a backgroundworker)
EDIT 2:
I've reduced the number of invokes by reducing the amount to write, but to increase delay giving the same effect of constant writing, just reducing the load.
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 500; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
Thread.Sleep(2);
}));
}
}
EDIT 3:
Sumeet's example works using
Application.DoEvents();
(notice the s, .DoEvent doesn't work, typo probably :P), writing multiple strings simultaneously & having them show the progress and not just the result.
So Code update again :)
*Using a new button to create 5 threads that write a random number to both textboxes
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 57; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
Thread.Sleep(5);
Application.DoEvents();
}));
}
}
private void btnNewThread_Click(object sender, EventArgs e)
{
Random random = new Random();
int[] randomNumber = new int[5];
for (int i = 0; i < 5; i++)
{
randomNumber[i] = random.Next(2, 9);
new Thread(() => writeText(txtOutput1, randomNumber[i-1].ToString())).Start();
new Thread(() => writeText(txtOutput2, randomNumber[i-1].ToString())).Start();
}
}
This solution works ! Have checked it.
The problem is you keep telling the UI thread to change the Text, but never letting it have time to show you the updated text.
To make your UI show the changed text, add the Application.DoEvents line like this :
textBox.Text += text;
Application.DoEvents();
p.s. : Remove the else block of your If / Else loop, it is redundant, and also as pointed by others there is not any use of creating those 2 Threads as all they are doing is post the message on the UI Thread itself.
You're still performing a single-threaded task, just re-launching it on the UI thread if needed.
for (int i = 0; i < 500; i++){
string text = ""+i;
textBox.BeginInvoke((MethodInvoker)delegate()
{
textBox.Text += text;
});
}
The problem is that you're starting a new thread, and then that new thread is doing nothing except adding one new task for the UI thread to process that does a lot of work. To keep your form responsive you need to have time where the UI thread is doing nothing, or at least not spending a significant amount of time doing any one task.
To keep the form responsive we need to have lots of little BeginInvoke (or Invoke) calls.
private void writeText(TextBox textBox, string text)
{
for (int i = 0; i < 500; i++)
{
Invoke(new MethodInvoker(() =>
{
textBox.Text += text;
}));
}
}
By having lots of little invoke calls it allows things like paint events, mouse move/click events, etc. to be handled in the middle of your operations. Also note that I removed the InvokeRequired call. We know that this method will be called from a non-UI thread, so there's no need for it.
You're defeating the purpose of using threads.
All your thread does is tell the UI thread to execute some code using BeginInvoke().
All of the actual work happens on the UI thread.
Either you're doing data processing or you're just trying to animate the UI.
For data processing you should do all the heavy lifting on a background thread and only update the UI occasionally. In your example a TextBox is particularly troublesome in this regard, as you're adding data to the underlying data model several hundred times and the UI element (a TextBox) takes longer to render each time. You must be careful about how often to update the UI so that processing for UI updates does not overwhelm data model updates. TextBoxes are nasty like that.
In the example below, a flag set during the paint event ensures that additional UI updates aren't queued until the TextBox has finished painting the last update:
string str = string.Empty;
public void DoStuff()
{
System.Threading.ThreadPool.QueueUserWorkItem(WorkerThread);
}
void WorkerThread(object unused)
{
for (int i = 0; i < 1000; i++)
{
str += "0";
if (updatedUI)
{
updatedUI = false;
BeginInvoke(new Action<string>(UpdateUI), str);
}
}
BeginInvoke(new Action<string>(UpdateUI), str);
}
private volatile bool updatedUI = true;
void textbox1_Paint(object sender, PaintEventArgs e) // event hooked up in Form constructor
{
updatedUI = true;
}
void UpdateUI(string str)
{
textBox1.Text = str;
}
On the other hand if UI animation is your goal then you probably ought to be using something other than a TextBox. It's just not designed to handle updates so frequently. There might be some optimizations to text rendering you could make for your specific use case.
You must never use a string in high volume applications. UI or not. Multi-threading or not.
You should use StringBuilder to accumulate the string. and then assign
tb.Text = sb.ToString();

create Wpf user controls in other thread

I am trying to create some UserControl(s) using another thread, and I am using code like this:
private void btnDemo_Click(object sender, RoutedEventArgs e)
{
Task tsk = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 3; i++)
{
MyControl sprite = new MyControl();
pnlTest.Children.Add(sprite);
}
});
}
But I am getting this exception in the UserControl constructor:
The calling thread must be STA, because many UI components require this.
I am not sure that I am using the right approach to do this. Please, Can you help me with this.
thanks.
The creating of the controls can be done on any Thread but Adding them to the GUI needs to be synchronized to the main Thread.
In this case, just 3 controls, forget about Tasks and just do it directly, single-threaded.
You can dispatch the operation of adding controls to the Children collection to the UI thread using Dispatcher:
private void btnDemo_Click(object sender, RoutedEventArgs e)
{
Task tsk = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 3; i++)
{
Dispatcher.BeginInvoke(new Action(() => {
MyControl sprite = new MyControl();
pnlTest.Children.Add(sprite);
}));
}
});
}
By calling BeginInvoke on Dispatcher you basically adding the operation to the queue to execute on the UI thread.

Categories