cannot get progressbar animation with code, progresschanged not called - c#

i'm trying to do the code here
http://www.dotnetperls.com/progressbar
Here is the code I have. I've drawn on a backgroundworker, and programmatically added a progress bar.
I've tried stepping over the code, and i've tried using messageboxes to see what is going on, and it looks like it is only executing one iteration of the for loop, which is for when i=0, and it seems it goes from that, to te done procedure, and the Progress procedure never gets invoked.
And the progress bar never changes value from 0.
When I want it to progress to 100.
I did try commenting out this line //Application.EnableVisualStyles(); in program.cs to remove a natural animation that the progerss bar has, but either way, commented or uncommented, jt's not even running every iteration of the for loop, it's quitting after i=0.. And there is no progress with the backgroundworker, or the progress bar.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace backgroundworker2
{
public partial class Form1 : Form
{
ProgressBar progressBar1 = new ProgressBar();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// progressBar1.BackColor = Color.Red;
this.Controls.Add(progressBar1);
//progressBar1.Value = 100;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// textBox1.Text = "0";
for (int i = 0; i < 100; i++)
{
// MessageBox.Show("a"+i);
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("done");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// textBox1.Text = Convert.ToString( e.ProgressPercentage);
progressBar1.Value = e.ProgressPercentage;
// MessageBox.Show("asdf");
}
}
}

In Form1_Load you run the BackgroundWorker but you haven't set it's WorkerReportsProgress property to true. The default is false. So you need to add this line before you call the RunWorkerAsync() method:
backgroundWorker1.WorkerReportsProgress = true;

Related

How to display images in pictureBox1 one by one?

maybe i need a timer ?
i want that before the image is saved or after saved but to display the images one by one.
now it's just doing the loop so it's not showing the designer at all until the loop will end.
using Accord.Video.FFMPEG;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;
namespace Extract_Frames
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(#"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
for (int i = 0; i < vFReader.FrameCount; i++)
{
Bitmap bmpBaseOriginal = vFReader.ReadVideoFrame();
//bmpBaseOriginal.Save(#"d:\frames\frame" + i + ".bmp");
pictureBox1.Image = bmpBaseOriginal;
//bmpBaseOriginal.Dispose();
}
vFReader.Close();
}
}
}
}
It's working for while but after some images it's throwing exception on the line :
pictureBox1.Image = bmpBaseOriginal;
the exception say the object is in use.
System.InvalidOperationException: 'Object is currently in use
Your code should probably be something like this:
private readonly Queue<Image> images = new Queue<Image>();
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(#"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
for (var i = 0; i < vFReader.FrameCount; i++)
{
images.Enqueue(vFReader.ReadVideoFrame());
}
// Not sure that this would be required as it might happen implicitly at the end of the 'using' block.
vFReader.Close();
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pictureBox1.Image = images.Dequeue();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Image.Dispose();
var image = images.Dequeue();
pictureBox1.Image = image;
if (images.Count == 0)
{
timer1.Stop();
}
}
All the Images are added to a queue and then dequeued one by one. The first one is displayed as soon as the loading has completed and the rest will be displayed at regular intervals. This code displayed each Image once and then destroys and discards it. If you want to display them all again when you reach the last then you can use a different type of collection and wrap back to the beginning when you get to the end.
EDIT:
I may have misunderstood a little what you were asking for. This code should display the Images as they're generated:
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(#"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
for (var i = 0; i < vFReader.FrameCount; i++)
{
backgroundWorker1.ReportProgress(0, vFReader.ReadVideoFrame());
}
// Not sure that this would be required as it might happen implicitly at the end of the 'using' block.
vFReader.Close();
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pictureBox1.Image?.Dispose();
pictureBox1.Image = (Image)e.UserState;
}
You can ditch the Timer and the Queue. My only concern is that this may lead to an OutOfMemoryException. If it does, you can call explicitly call GC.Collect intermittently, e.g. every 100 frames.

How to update an Readonly textbox constantly C#

I am wondering how to constantly update an readonly textbox.
The text box displays a text that always changes.
My problem is if I create an loop the application won't start and if I start the loop using a button my application freezes and only it only runs the loop.
I also can't use a new thread or the thread that I use to change the variables that are displayed within the text because in this case I just get an error System.InvalidOperationException
I was searching for anwser but I couldn't find one.
When using a thread you have to cause your ui update work to run on the UI thread, and that's where you use an "invoke".
There are many ways to achieve your goal, I'll show you two ways you can do it:
using a thread (BackgroundWorker is just a fancier way to do that)
a Timer (it might be overkill to use a thread just to update a
counter if that is what you are intending).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
bool m_shutdown = false;
int m_counter = 0;
Timer m_timer = new Timer();
BackgroundWorker m_backgroundworker = new BackgroundWorker();
bool m_usetimerway = false; // change this to true to try the timer way
Action m_actionupdatecounter;
public Form1()
{
InitializeComponent();
m_actionupdatecounter = new Action(() =>
{
UpdateCounter();
});
}
private void Form1_Load(object sender, EventArgs e)
{
if (m_usetimerway)
{
m_timer.Interval = 50;
m_timer.Tick += M_timer_Tick;
m_timer.Enabled = true;
}
else
{
m_backgroundworker.DoWork += M_backgroundworker_DoWork;
m_backgroundworker.RunWorkerCompleted += M_backgroundworker_RunWorkerCompleted;
m_backgroundworker.RunWorkerAsync();
}
}
void UpdateCounter()
{
if (this.InvokeRequired)
{
// Get it to be run on the UI thread
this.BeginInvoke( m_actionupdatecounter );
}
else
{
m_counter++;
textBoxCounter.Text = string.Format("{0}", m_counter);
}
}
private void M_timer_Tick(object sender, EventArgs e)
{
// This is already on the UI thread because it's a "WinForms" timer
UpdateCounter();
}
private void M_backgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
while (!m_shutdown)
{
UpdateCounter();
System.Threading.Thread.Sleep(50);
}
}
private void M_backgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
m_shutdown = true;
// To have a more graceful shutdown, you might want to wait for the
// background worker to have "completed" before you actually exit
// your winforms app.
}
}
}

Thread isn't Starting

I'm new to c# and Multithreading. I have this code to getting started with Multithreading but clock tick isn't getting started. What's wrong with this code? No error occurs because its a logical error I guess. Any help would be appreciated.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace Implementing_Databases
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
picturebox1.Location=new Point(0,20);
pictureBox2.Location = new Point(0, 60);
}
int B1 = 0;
int B2 = 0;
private void Form1_Load(object sender, EventArgs e)
{
Thread Th1 = new Thread(Go1);
Thread Th2 = new Thread(Go2);
Th1.Start();
Th2.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
picturebox1.Left = B1;
B1 += 5;
}
private void timer2_Tick(object sender, EventArgs e)
{
pictureBox2.Left = B2;
B2 += 5;
}
void Go1()
{
timer1.Start();
}
void Go2()
{
timer2.Start();
}
}
}
First of all, try declaring threads as properties of form rather than declaring them as local function variables. Because otherwise they may be collected by GC straight away after Load handler exits.
Secondly, UI not updating may be due to the fact that you cannot update UI data from non GUI thread. See InvokeRequired/Invoke feature of WinForms programming. See https://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired(v=vs.110).aspx for more details

Increment with timer.Tick not working

I have a metronome project set up. I have a tap button which should check the tempo of your beat and average it out. Every bit of math works properly because I checked it with a calculator. Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
namespace Metronome
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void timer3_Tick(object sender, EventArgs e)
{
// Convert tempo to timer1.Tick (miliseconds between each beat)
timer1.Interval = Convert.ToInt32(60000 / numericUpDown1.Value);
}
private void button1_Click(object sender, EventArgs e)
{
// Play / Pause button
if (button1.Text == "Go!") { timer1.Enabled = true; button1.Text = "Stop!"; }
else if (button1.Text == "Stop!") { timer1.Enabled = false; button1.Text = "Go!"; }
}
private void timer1_Tick(object sender, EventArgs e)
{
// The 'ding' sound for the metronome
SystemSounds.Beep.Play();
}
private void button2_Click(object sender, EventArgs e)
{
// Set the tempo to be the average of the convertion from miliseconds between 2 beats and the current tempo
if (timer2.Enabled) { numericUpDown1.Value = ((60000 / Tap) + numericUpDown1.Value) / 2; Tap = 0; }
else timer2.Enabled = true;
}
int Tap = 0;
private void timer2_Tick(object sender, EventArgs e)
{
// Get the amount of miliseconds between each beat
Tap++;
}
private void button3_Click(object sender, EventArgs e)
{
// Reset the tap timer
timer2.Enabled = false;
Tap = 0;
}
}
}
The problem is in timer2_Tick, because it should add 1 to Tap every milisecond, instead, when I tried it it goes to a tiny number like 20 or 30. How can I fix this?
There is a really good article I always rely on when selecting which timer to use:
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
I would suggest using one of the threaded options. Specifically, the article says of the the windows forms timer (System.Windows.Forms.Timer):
If you're looking for a metronome, you've come to the wrong place.
If you only need to check the amount of time passed between button taps, use a StopWatch. It gives you a high precision timing mechanism. There is no need for you to count milliseconds yourself.

Passing argument into backgroundWorker (for use as a Cancel button)

I'm new to C# and object-oriented programming in general. I've been trying to implement a "Cancel" button into my GUI so that the user can stop it mid-process.
I read this question: How to implement a Stop/Cancel button? and determined that a backgroundWorker should be a good option for me, but the example given doesn't explain how to hand arguments to the backgroundWorker.
My problem is that I do not know how to pass an argument into backgroundWorker such that it will stop the process; I have only been able to get backgroundWorker to stop itself.
I created the following code to try to learn this, where my form has two buttons (buttonStart and buttonStop) and a backgroundWorker (backgroundWorkerStopCheck):
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Timers;
namespace TestBackgroundWorker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set the background worker to allow the user to stop the process.
backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
}
private System.Timers.Timer myTimer;
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
//If cancellation is pending, cancel work.
if (backgroundWorkerStopCheck.CancellationPending)
{
e.Cancel = true;
return;
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
// Notify the backgroundWorker that the process is starting.
backgroundWorkerStopCheck.RunWorkerAsync();
LaunchCode();
}
private void buttonStop_Click(object sender, EventArgs e)
{
// Tell the backgroundWorker to stop process.
backgroundWorkerStopCheck.CancelAsync();
}
private void LaunchCode()
{
buttonStart.Enabled = false; // Disable the start button to show that the process is ongoing.
myTimer = new System.Timers.Timer(5000); // Waste five seconds.
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.Enabled = true; // Start the timer.
}
void myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
buttonStart.Enabled = true; // ReEnable the Start button to show that the process either finished or was cancelled.
}
}
}
The code, if it worked properly, would just sit there for five seconds after the user clicked "Start" before re-enabling the Start button, or would quickly reactivate the Start button if the user clicked "Stop".
There are two problems with this code that I am not sure how to handle:
1) The "myTimer_Elapsed" method results in an InvalidOperationException when it attempts to enable the Start button, because the "cross-thread operation was not valid". How do I avoid cross-thread operations?
2) Right now the backgroundWorker doesn't accomplish anything because I don't know how to feed arguments to it such that, when it is canceled, it will stop the timer.
I'd appreciate any assistance!
First of all, the problem to avoid "cross-thread operation was not valid" is use Invoke on controls. You cannot use a control from a different thread.
About the second issue, I would implement it in the following way. This is a minimum background worker implementation with cancel support.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set the background worker to allow the user to stop the process.
backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork);
}
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
try
{
for (int i = 0; i < 50; i++)
{
if (backgroundWorkerStopCheck.CancellationPending)
{
// user cancel request
e.Cancel = true;
return;
}
System.Threading.Thread.Sleep(100);
}
}
finally
{
InvokeEnableStartButton();
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
//disable start button before launch work
buttonStart.Enabled = false;
// start worker
backgroundWorkerStopCheck.RunWorkerAsync();
}
private void buttonStop_Click(object sender, EventArgs e)
{
// Tell the backgroundWorker to stop process.
backgroundWorkerStopCheck.CancelAsync();
}
private void InvokeEnableStartButton()
{
// this method is called from a thread,
// we need to Invoke to avoid "cross thread exception"
if (this.InvokeRequired)
{
this.Invoke(new EnableStartButtonDelegate(EnableStartButton));
}
else
{
EnableStartButton();
}
}
private void EnableStartButton()
{
buttonStart.Enabled = true;
}
}
internal delegate void EnableStartButtonDelegate();
}
About passing arguments to the worker, you can pass any object in the RunWorkerAsync() method, and its reveived in the backgroundWorkerStopCheck_DoWork method:
...
backgroundWorkerStopCheck.RunWorkerAsync("hello");
...
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
string argument = e.Argument as string;
// argument value is "hello"
...
}
Hope it helps.
try this example and you will see how to pass data to and from the BackgroundWorker:
public partial class Form1 : Form
{
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.WorkerSupportsCancellation = true;
}
private void button1_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
btnCancel.Enabled = true;
double[] data = new double[1000000];
Random r = new Random();
for (int i = 0; i < data.Length; i++)
data[i] = r.NextDouble();
bw.RunWorkerAsync(data);
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnStart.Enabled = true;
btnCancel.Enabled = false;
if (!e.Cancelled)
{
double result = (double)e.Result;
MessageBox.Show(result.ToString());
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
double[] data = (double[])e.Argument;
for (int j = 0; j < 200; j++)
{
double result = 0;
for (int i = 0; i < data.Length; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
result += data[i];
}
e.Result = result;
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
bw.CancelAsync();
btnStart.Enabled = true;
btnCancel.Enabled = false;
}
}

Categories