ProgressBar
-
private void button1\_Click(object sender, RoutedEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; for (int i = 0; i < 100; i++) { SolidColorBrush brush = new SolidColorBrush(); Line line = new Line(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); } }
I would like to use a ProgressBar for this loop, but when I tried it my ProgressBar only updated itself once after the loop had been completed when it went from 0% to 100%. I reckon canvas1.Children.Add(line) functions like a stack, maybe I need to override some virtual method to make the ProgessBar show progress. Could anybody here give me a hint on how to do this? Thankyou, Ranger. PS, What I tried was in the i-loop a command
this.progressBar.Value = i;
I tried my code in a sample program that only had an i-loop that did nothing and there my progressBar worked fine... -
private void button1\_Click(object sender, RoutedEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; for (int i = 0; i < 100; i++) { SolidColorBrush brush = new SolidColorBrush(); Line line = new Line(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); } }
I would like to use a ProgressBar for this loop, but when I tried it my ProgressBar only updated itself once after the loop had been completed when it went from 0% to 100%. I reckon canvas1.Children.Add(line) functions like a stack, maybe I need to override some virtual method to make the ProgessBar show progress. Could anybody here give me a hint on how to do this? Thankyou, Ranger. PS, What I tried was in the i-loop a command
this.progressBar.Value = i;
I tried my code in a sample program that only had an i-loop that did nothing and there my progressBar worked fine...Ranger49 wrote:
tried my code in a sample program that only had an i-loop that did nothing and there my progressBar worked fine...
Maybe it just appeared to work fine - a loop from 0 to 99 is pretty fast on modern machines. :) When I ran your code, even the lines didn't show up until the end of the loop. This is because you're hogging the UI thread for the duration of the loop. It may be a better idea to do busy work on a separate thread. Here's an example using a BackgroundWorker thread (I added a Sleep() to slow things down a bit since on my machine this loop finishes faster than I can see):
BackgroundWorker bw = new BackgroundWorker(); public Window1() { InitializeComponent(); bw.WorkerReportsProgress = true; bw.DoWork += new DoWorkEventHandler(bw\_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw\_ProgressChanged); } private void button1\_Click(object sender, RoutedEventArgs e) { if (!bw.IsBusy) bw.RunWorkerAsync(); } void bw\_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 100; i++) { bw.ReportProgress(i); Thread.Sleep(50); } } void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; SolidColorBrush brush = new SolidColorBrush(); Line line = new Line(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); progressbar1.Value = e.ProgressPercentage; }
*edit* Funny side note: Taking the Sleep() call out of my example makes it run too f
-
Ranger49 wrote:
tried my code in a sample program that only had an i-loop that did nothing and there my progressBar worked fine...
Maybe it just appeared to work fine - a loop from 0 to 99 is pretty fast on modern machines. :) When I ran your code, even the lines didn't show up until the end of the loop. This is because you're hogging the UI thread for the duration of the loop. It may be a better idea to do busy work on a separate thread. Here's an example using a BackgroundWorker thread (I added a Sleep() to slow things down a bit since on my machine this loop finishes faster than I can see):
BackgroundWorker bw = new BackgroundWorker(); public Window1() { InitializeComponent(); bw.WorkerReportsProgress = true; bw.DoWork += new DoWorkEventHandler(bw\_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw\_ProgressChanged); } private void button1\_Click(object sender, RoutedEventArgs e) { if (!bw.IsBusy) bw.RunWorkerAsync(); } void bw\_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 100; i++) { bw.ReportProgress(i); Thread.Sleep(50); } } void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; SolidColorBrush brush = new SolidColorBrush(); Line line = new Line(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); progressbar1.Value = e.ProgressPercentage; }
*edit* Funny side note: Taking the Sleep() call out of my example makes it run too f
Somehow, it seems to me, that the DoWork method should do the work and that the ProgressChanged method should update the progressBar. Unfortunately this doesn't work, I get an error message at runtime.
public void bw\_DoWork(object sender, DoWorkEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; Line line = new Line(); for (int i = 0; i < 100; i++) { SolidColorBrush brush = new SolidColorBrush(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); bw.ReportProgress(i); } } public void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressbar1.Value = e.ProgressPercentage; }
When I tried this, I got an exception in the line:
Line line = new Line();
because the thread doesn't have enough resources it should be a STA Thread, which means that this method uses up so much capacity that it needs to be the only Thread running. I am new to this. I never made a program that used more than one thread, I tried but didn't succeed. If anybody has any pointers for me, I would appreciate it. Ranger.modified on Monday, June 29, 2009 3:44 PM
-
Somehow, it seems to me, that the DoWork method should do the work and that the ProgressChanged method should update the progressBar. Unfortunately this doesn't work, I get an error message at runtime.
public void bw\_DoWork(object sender, DoWorkEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; Line line = new Line(); for (int i = 0; i < 100; i++) { SolidColorBrush brush = new SolidColorBrush(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); bw.ReportProgress(i); } } public void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressbar1.Value = e.ProgressPercentage; }
When I tried this, I got an exception in the line:
Line line = new Line();
because the thread doesn't have enough resources it should be a STA Thread, which means that this method uses up so much capacity that it needs to be the only Thread running. I am new to this. I never made a program that used more than one thread, I tried but didn't succeed. If anybody has any pointers for me, I would appreciate it. Ranger.modified on Monday, June 29, 2009 3:44 PM
Ranger49 wrote:
should the code to compute the fractal be put in the DoWork method, where the progressbar is in the ProgressChanged method?
Yes. My example was over-simplified, and putting the code to create the lines in the ProgressChaged event handler was out of necessity. I did that because creating lines in the BackgroundWorker thread throws an exception because the BackroundWorker thread is an MTA model thread and creating WPF elements needs to be on a STA model thread. Additionally, even if you create the lines in a STA thread, you can't add them to the canvas because you can only access UI elements on the UI thread, and a line created on a background thread can't be added to a canvas created on the UI thread. Catch-22? Anyway, you'd want to do as much of the calculation as you can on the background thread (i.e. in the DoWork handler) and you have to do the UI stuff on the UI thread (the BackgroundWorker.ProgressChanged event occurs on the UI thread).
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Somehow, it seems to me, that the DoWork method should do the work and that the ProgressChanged method should update the progressBar. Unfortunately this doesn't work, I get an error message at runtime.
public void bw\_DoWork(object sender, DoWorkEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; Line line = new Line(); for (int i = 0; i < 100; i++) { SolidColorBrush brush = new SolidColorBrush(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); bw.ReportProgress(i); } } public void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressbar1.Value = e.ProgressPercentage; }
When I tried this, I got an exception in the line:
Line line = new Line();
because the thread doesn't have enough resources it should be a STA Thread, which means that this method uses up so much capacity that it needs to be the only Thread running. I am new to this. I never made a program that used more than one thread, I tried but didn't succeed. If anybody has any pointers for me, I would appreciate it. Ranger.modified on Monday, June 29, 2009 3:44 PM
The issue here is that you are trying to update the line in a none-UI thread. You have two choices; put the UI work in the progress changed event, or invoke changes to force them onto the UI thread. If it was my choice, I'd do the UI stuff in the event.
"WPF has many lovers. It's a veritable porn star!" - Josh Smith
As Braveheart once said, "You can take our freedom but you'll never take our Hobnobs!" - Martin Hughes.
-
Ranger49 wrote:
should the code to compute the fractal be put in the DoWork method, where the progressbar is in the ProgressChanged method?
Yes. My example was over-simplified, and putting the code to create the lines in the ProgressChaged event handler was out of necessity. I did that because creating lines in the BackgroundWorker thread throws an exception because the BackroundWorker thread is an MTA model thread and creating WPF elements needs to be on a STA model thread. Additionally, even if you create the lines in a STA thread, you can't add them to the canvas because you can only access UI elements on the UI thread, and a line created on a background thread can't be added to a canvas created on the UI thread. Catch-22? Anyway, you'd want to do as much of the calculation as you can on the background thread (i.e. in the DoWork handler) and you have to do the UI stuff on the UI thread (the BackgroundWorker.ProgressChanged event occurs on the UI thread).
Mark Salsbery Microsoft MVP - Visual C++ :java:
I will read chapters about Threading in the books I use to learn about WPF and C# 2008. This matter is really tricky, but that this is a challenge at the same time. I reckon it isn't possible to turn the DoWork method into the UI Thread and the ProgressChanged in the backgroundworker Thread? But I find this really interesting. Thanks, Ranger.
-
Somehow, it seems to me, that the DoWork method should do the work and that the ProgressChanged method should update the progressBar. Unfortunately this doesn't work, I get an error message at runtime.
public void bw\_DoWork(object sender, DoWorkEventArgs e) { Random rnd = new Random(); Pen p = new Pen(); byte red, green, blue; Line line = new Line(); for (int i = 0; i < 100; i++) { SolidColorBrush brush = new SolidColorBrush(); red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; brush.Color = col; line.X2 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); line.X1 = rnd.Next(0, (int)canvas1.ActualWidth); line.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); line.Stroke = brush; line.StrokeThickness = 1.0; canvas1.Children.Add(line); bw.ReportProgress(i); } } public void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressbar1.Value = e.ProgressPercentage; }
When I tried this, I got an exception in the line:
Line line = new Line();
because the thread doesn't have enough resources it should be a STA Thread, which means that this method uses up so much capacity that it needs to be the only Thread running. I am new to this. I never made a program that used more than one thread, I tried but didn't succeed. If anybody has any pointers for me, I would appreciate it. Ranger.modified on Monday, June 29, 2009 3:44 PM
I have a class that is the equivalent of BackgroundWorker only STA, but even that won't help you here. Even if creating the Line succeeded, you can't access UI objects on a different thread. That means accessing the canvas will fail as well. Here's an updated example where I've kept non-UI stuff in the DoWork handler and moved UI stuff to the ProgressChanged handler. I added a LineDescriptor helper class to pass line info from the background thread to the UI thread:
public partial class Window1 : Window { BackgroundWorker bw = new BackgroundWorker(); public Window1() { InitializeComponent(); bw.WorkerReportsProgress = true; bw.DoWork += new DoWorkEventHandler(bw\_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw\_ProgressChanged); } private void button1\_Click(object sender, RoutedEventArgs e) { if (!bw.IsBusy) bw.RunWorkerAsync(); } void bw\_DoWork(object sender, DoWorkEventArgs e) { `// Background thread - no UI element access allowed here!` LineDescriptor ld = new LineDescriptor(); for (int i = 0; i < 100; i++) { Random rnd = new Random(); //Pen p = new Pen(); byte red, green, blue; red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; ld.Color = col; ld.X2 = rnd.Next(0, (int)canvas1.ActualWidth); ld.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); ld.X1 = rnd.Next(0, (int)canvas1.ActualWidth); ld.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); bw.ReportProgress(i + 1, ld); `//<-- bug fix - added 1 to i` :) Thread.Sleep(25); } } void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { `// UI thread - UI element access allowed here!` LineDescriptor ld = e.UserState as LineDescriptor; Line line = new Line(); line.X1 = ld.X1; line.Y1 = ld.Y1; line.X2 = ld.X2;
-
I have a class that is the equivalent of BackgroundWorker only STA, but even that won't help you here. Even if creating the Line succeeded, you can't access UI objects on a different thread. That means accessing the canvas will fail as well. Here's an updated example where I've kept non-UI stuff in the DoWork handler and moved UI stuff to the ProgressChanged handler. I added a LineDescriptor helper class to pass line info from the background thread to the UI thread:
public partial class Window1 : Window { BackgroundWorker bw = new BackgroundWorker(); public Window1() { InitializeComponent(); bw.WorkerReportsProgress = true; bw.DoWork += new DoWorkEventHandler(bw\_DoWork); bw.ProgressChanged += new ProgressChangedEventHandler(bw\_ProgressChanged); } private void button1\_Click(object sender, RoutedEventArgs e) { if (!bw.IsBusy) bw.RunWorkerAsync(); } void bw\_DoWork(object sender, DoWorkEventArgs e) { `// Background thread - no UI element access allowed here!` LineDescriptor ld = new LineDescriptor(); for (int i = 0; i < 100; i++) { Random rnd = new Random(); //Pen p = new Pen(); byte red, green, blue; red = (byte)rnd.Next(0, 255); green = (byte)rnd.Next(0, 255); blue = (byte)rnd.Next(0, 255); Color col = new Color(); col.R = red; col.G = green; col.B = blue; col.A = 255; ld.Color = col; ld.X2 = rnd.Next(0, (int)canvas1.ActualWidth); ld.Y2 = rnd.Next(0, (int)canvas1.ActualHeight); ld.X1 = rnd.Next(0, (int)canvas1.ActualWidth); ld.Y1 = rnd.Next(0, (int)canvas1.ActualHeight); bw.ReportProgress(i + 1, ld); `//<-- bug fix - added 1 to i` :) Thread.Sleep(25); } } void bw\_ProgressChanged(object sender, ProgressChangedEventArgs e) { `// UI thread - UI element access allowed here!` LineDescriptor ld = e.UserState as LineDescriptor; Line line = new Line(); line.X1 = ld.X1; line.Y1 = ld.Y1; line.X2 = ld.X2;