C# Animation Freezes the program - c#

For some reason, This freezes the program:
private void Form1_Shown(object sender, EventArgs e)
{
int x = 1;
var frame1 = pictureBox2.BackgroundImage;
var frame2 = pictureBox3.BackgroundImage;
while(x < 2)
{
pictureBox1.BackgroundImage = frame1;
pictureBox1.BackgroundImage = frame2;
}
}
why?
pictureBox2 contains the first frame and pictureBox3 contains the second.
pictureBox1 contains the 'animation'. (as seen in code)
EDIT: I don't want it to animate ONCE, I want it to animate FOREVER.

Here's one way...
Mark your Shown() event with async, then use await Task.Delay() between frames:
private async void Form1_Shown(object sender, EventArgs e)
{
bool first = true;
var frame1 = pictureBox2.BackgroundImage;
var frame2 = pictureBox3.BackgroundImage;
while (true)
{
pictureBox1.BackgroundImage = first ? frame1 : frame2;
first = !first;
await Task.Delay(500); // 1/2 second delay <-- set it to your desired delay between frames
}
}
---------- Edit ----------
An alternate approach using a Timer and an IEnumerator. This would be a good way to go if you had more than just two frames:
private IEnumerator<Image> frames;
private System.Windows.Forms.Timer tmr;
private void Form1_Shown(object sender, EventArgs e)
{
List<Image> lstFrames = new List<Image>();
lstFrames.Add(pictureBox2.BackgroundImage);
lstFrames.Add(pictureBox3.BackgroundImage);
lstFrames.Add(pictureBox4.BackgroundImage);
// etc...
frames = lstFrames.GetEnumerator();
DisplayNextFrame();
tmr = new System.Windows.Forms.Timer();
tmr.Interval = 500;
tmr.Tick += Tmr_Tick;
tmr.Start();
}
private void Tmr_Tick(object sender, EventArgs e)
{
DisplayNextFrame();
}
private void DisplayNextFrame()
{
if (!frames.MoveNext())
{
frames.Reset();
frames.MoveNext();
}
pictureBox1.BackgroundImage = frames.Current;
}

In your case, the value of x is always 1 and that is why the while loop keep iterating and the program goes in an infinite loop.
Solution:
You should use BackgroundWorker to run the code as an Asynchronous
Insert a new BackgroundWorker into your form.
Insert your function into DoWork event.
private void bWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var frame1;
var frame2;
if (pictureBox2.InvokeRequired) { pictureBox2.Invoke((MethodInvoker)delegate { frame1 = pictureBox2.BackgroundImage; }); } else { frame1 = pictureBox2.BackgroundImage; }
if (pictureBox3.InvokeRequired) { pictureBox3.Invoke((MethodInvoker)delegate { frame2 = pictureBox3.BackgroundImage; }); } else { frame2 = pictureBox3.BackgroundImage; }
while (true)
{
if (pictureBox1.InvokeRequired) { pictureBox1.Invoke((MethodInvoker)delegate { pictureBox1.BackgroundImage = frame1; }); } else { pictureBox1.BackgroundImage = frame1; }
if (pictureBox1.InvokeRequired) { pictureBox1.Invoke((MethodInvoker)delegate { pictureBox1.BackgroundImage = frame2; }); } else { pictureBox1.BackgroundImage = frame2; }
}
}
Now, Start running BackgroundWorker.
bWorker1.RunWorkerAsync();

I think you should add some time delays between the frame1 and frame2 inside while loop to see the animation work.

Your loop runs forever and never terminates.
There's no termination clause for the while (x < 2) loop hence your program freezes; x is always smaller than 2. If you don't allow the application to breath it freezes. Try this:
private void Form1_Shown(object sender, EventArgs e)
{
var frame1 = pictureBox2.BackgroundImage;
var frame2 = pictureBox3.BackgroundImage;
while(true)
{
pictureBox1.BackgroundImage = frame1;
Application.DoEvents(); // give the thread room to run background tasks
pictureBox1.BackgroundImage = frame2;
Application.DoEvents(); // give the thread room to run background tasks
}
}
DoEvents() is just one option, maybe not the best for your case. You're probably better off writing a proper timer which switches between the two images ever 40 milliseconds or whatever your animation speed is supposed to be.

Create a parallel thread for your animiation:
private void Form1_Shown(object sender, EventArgs e)
{
//create parallel thread
Thread animationThread = new Thread(Animation);
animationThread.Start();
}
//create a new method for animation to run on a parallel thread
private void Animation()
{
int x = 1;
var frame1 = pictureBox1.Image;
var frame2 = pictureBox2.Image;
while (x < 2)
{
this.Invoke(new Action(() => pictureBox3.Image = frame1));
this.Invoke(new Action(() => pictureBox3.Refresh()));
Thread.Sleep(100); //you must keep a dealy other wise it will consume too much processing
this.Invoke(new Action(() => pictureBox3.Image = frame2));
this.Invoke(new Action(() => pictureBox3.Refresh()));
}
}

Related

C# Using task and yield to keep UI informed of a running process

Is it bad practice to write code like this. What I want to accomplish is that a user can press a button on a control. The button starts some kind of analyzing process and for each item done it shows a result to the user.
private IEnumerable<int> AnalyzeItems() {
for(int i = 0; i < 1000; i++) {
Thread.Sleep(500);
yield return i;
}
}
private void PerformTask_Click(object sender, EventArgs e) {
Task.Run(() => {
foreach (var item in AnalyzeItems()) {
ResultLog.Invoke((Action)delegate() { ResultLog.Text += item.ToString(); });
}
});
}
why do not use Backgroundworker?
First setup the backgroundworker properties to:
WorkerReportsProgress = true
WorkerSupportsCancellation = true
This is the code:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
for (int i = 0; i < 1000; i++) {
Thread.Sleep(500);
if (backgroundWorker1.CancellationPending) {
e.Cancel = true;
break;
}
backgroundWorker1.ReportProgress(i / 10, "step " + i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
label1.Text = e.UserState.ToString();
progressBar1.Value = e.ProgressPercentage;
}
private void button1_Click(object sender, EventArgs e) {
cancelButton.Focus();
button1.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void cancelButton_Click(object sender, EventArgs e) {
backgroundWorker1.CancelAsync();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
button1.Enabled = true;
if (e.Error != null) {
MessageBox.Show(e.Error.Message, "Unexpected error");
}
if (e.Cancelled) {
MessageBox.Show("Process stopped by the user", "Cancelled");
}
label1.Text = "Press start";
progressBar1.Value = progressBar1.Minimum;
}
}
Is your approach bad practice? It depends.
If you don't expect your code inside Task.Run to throw any exceptions and you want to continue doing something else, then your code is ok. However, if you want to capture any possible exceptions and wait for the process to finish without freezing UI, then you might want to consider using async/await.
private async void PerformTask_Click(object sender, EventArgs e) {
try
{
await Task.Run(() => {
foreach (var item in AnalyzeItems()) {
ResultLog.Invoke((Action)delegate() { ResultLog.Text += item.ToString(); });
}
});
}
catch(Exception ex)
{
// handle...
}
}
Alternative approach would be to use IProgress<T>. This allows for easy separation of long running work and updating UI. Please note that you shouldn't call this method too often, because
This will put too much work on UI thread resulting in UI freeze.
If you pass any valuetype to IProgress<T>.Report method, then it gets copied. If you call this too often, you risk running garbage collector very often resulting in even bigger freezes.
All of this means that you should utilize IProgress only for truly long running work.
Now that we have it all out of the way, here is a sample of how you could notify users about progress of analyzed items:
private double _currentProgress;
public double CurrentProgress {
get => _currentProgress;
set
{
_currentProgress = value;
NotifyPropertyChanged();
}
}
private async void PerformTask_Click(object sender, EventArgs e)
{
var progress = new Progress<double>();
progress.ProgressChanged += (sender, p) => CurrentProgress = p;
await Task.Run(() => AnalyzeItems(Enumerable.Range(0, 5000).ToList(), progress));
}
private void AnalyzeItems(List<int> items, IProgress<double> progress)
{
for (int itemIndex = 0; itemIndex < items.Count; itemIndex++)
{
// Very long running CPU work.
// ...
progress.Report((double)itemIndex * 100 / items.Count);
}
}
If AnalyzeItems takes less than 100 ms for individual item, then you don't want to report after every finished item (see why above). You can decide how often you want to update status like this:
private void AnalyzeItems(List<int> items, IProgress<double> progress)
{
var lastReport = DateTime.UtcNow;
for (int itemIndex = 0; itemIndex < items.Count; itemIndex++)
{
// Very long running work.
Thread.Sleep(10);
// Tell the user what the current status is every 500 milliseconds.
if (DateTime.UtcNow - lastReport > TimeSpan.FromMilliseconds(500))
{
progress.Report((double)itemIndex * 100 / items.Count);
lastReport = DateTime.UtcNow;
}
}
}
If you have really a lot of very fast iterations, you may want to consider changing DateTime.Now to something else.

Running a progress bar using Background Controller in c#

I don't have a thorough knowledge about controlling threads in c#. I want to create a progress bar while running a method.
I have two forms:
Form1 is the form which show up as you run the app. It has a button called btnScrape. When it is clicked the method should be called, the form with the progress bar should show up. Form1 should be disabled to the user until the progress bar is completed.
ProgressBarForm - this has the progress bar and a label.
The code is as follows.
//In Form1.cs I have a button.
private void btnScrape_Click(object sender, EventArgs e)
{
//gather data for folloeing parameters from the form.
Controller cntrlr = new Controller(urlFilePath, destinationPath, destinationfilename,cmbDepth.SelectedIndex);
cntrlr.Vmain(); // this is the method in controller class. while this is running i want show the progress bar.
}
// in Contrller class
class Controller{
List<string> urlList = null;
URLFileReader urlFileReader = null;
HTTPWorker httpWorker = null;
SourceReader srcreader = null;
ReportWriter reportWriter = null;
string urlFilePath, destinationPath, destinationFileName;
int depth;
public Controller(string urlFilePath,string destinationPath,string destinationFileName,int depth)
{
this.urlFilePath = urlFilePath;
this.destinationPath = destinationPath;
this.destinationFileName = destinationFileName;
this.urlList = new List<string>();
this.urlFileReader = new URLFileReader();
this.httpWorker = new HTTPWorker(this.destinationPath);
this.reportWriter = new ReportWriter(this.destinationPath,this.destinationFileName);
this.srcreader = new SourceReader(this.reportWriter);
this.depth = depth;
}
//this is the method
public void Vmain(){
this.urlFileReader.ReadURL(urlFilePath);
this.urlList = urlFileReader.geturlList();
string pageSrc;
foreach (string requestUrl in urlList)
{
//do sruff for requestUrl
//the progressbar should move as the urlList iterate.
//additionally i want the label on the top of progress bar to display the current "requestUrl"
//as the urlList is over i want quit from the progressbar window and come back to Form1. Till the progrss bar is completed Form1 should be disabled for the user.
}
}
}
Please explain what is happening there and give a working code if you can. Thank you in advance. There were not any perfect answer that worked for me, even though I spent two days for this. I tried with BackgroundWorkerand threads. But no solution found. :(
In the main form you could use this code:
private Progressfrm _loadForm;
private void ShowProgress()
{
ToggleForm();
_loadForm = new Progressfrm();
_loadForm.ShowDialog();
var tcheck = new Thread(CheckLoadedProgress);
tcheck.Start();
//do stuff here
}
private void CheckLoadedProgress()
{
while (_loadForm.IsAccessible) { }
ToggleForm();
}
private void ToggleForm()
{
Invoke(new Action(() => Enabled = !Enabled));
}
private void btnScrape_Click(object sender, EventArgs e)
{
var tform = new Thread(ShowProgress);
tform.Start();
}
Then the Progress-Form will appear until it is filled:
private ProgressBar _progressBar;
private void Progressfrm_Shown(object sender, EventArgs e)
{
_progressBar = new ProgressBar { Size = new Size(100, 20), Location = new Point(10, 10) };
Controls.Add(_progressBar);
_progressBar.Show();
Refresh();
LoadProgress();
}
private void LoadProgress()
{
while (_progressBar.Value < 100)
{
_progressBar.Value++;
Thread.Sleep(100);
}
Close();
}
On this Form you have to add the Event Shown and add the code like in my example. Hope this helps.
Use BackgroundWorker class to output progressBar and statusLabel changes:
BackgroundWorker bgw;
private void btnScrape_Click(object sender, EventArgs e)
{
bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.RunWorkerAsync();
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(100);
bgw.ReportProgress(i);
}
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
This is only an example how to update controls in an asynchron way.

Windows form progress bar not working?

I have a button .It will open a file and process a file .I want to show the progress bar while processing the file .
when i am doing .its working
public MainframeDataExchangeTool()
{
InitializeComponent();
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.Visible = false;
_Random = new Random();
InitializeBackgroundWorker();
}
private void InitializeBackgroundWorker()
{
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.DoWork += (sender, e) => ((MethodInvoker)e.Argument).Invoke();
_BackgroundWorker.ProgressChanged += (sender, e) =>
{
_ProgressBar.Style = ProgressBarStyle.Continuous;
_ProgressBar.Value = e.ProgressPercentage;
};
_BackgroundWorker.RunWorkerCompleted += (sender, e) =>
{
if (_ProgressBar.Style == ProgressBarStyle.Marquee)
{
_ProgressBar.Visible = false;
}
};
}
In my button click i am doing
private void btnOpenScriptFile_Click(object sender, EventArgs e)
{
try
{
loadScriptFlDlg.Filter = Constants.SCRIPT_FILE_FILTER;
loadScriptFlDlg.FilterIndex = 3;
loadScriptFlDlg.RestoreDirectory = true;
loadScriptFlDlg.FileName = string.Empty;
DialogResult objDialogResult = loadScriptFlDlg.ShowDialog();
if (objDialogResult.Equals(DialogResult.OK))
{
_BackgroundWorker.RunWorkerAsync(new MethodInvoker(() =>
{
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
_BackgroundWorker.ReportProgress(i);
}
}));
EnableDisableControls("OpenScript");
string strScriptError = LoadScriptFromFile(loadScriptFlDlg.FileName);///loading will taking time but progress bar not showing
Basically progress bar is showing at the end of data load but not while loading the data
You cannot see the progress as UI thread cannot update UI because it is busy loading your file. You must call LoadScriptFromFile from the background worker and keep UI thread free to process events and update UI.
I used this in my project with 2 methode (exemple):
1* Invoke(new Action(() => _ProgressBar.Visible = true));
2* or use Application.DoEvents() after your _BackgroundWorker.ReportProgress(i);
Surprised the compiler didn't throw a hissy fit...because you need an extra }.
You open 4 levels of nesting, but only close 3.
I have modified your code as shown below:
private void btnOpenScriptFile_Click(object sender, EventArgs e)
{
try
{
if (objDialogResult.Equals(DialogResult.OK))
{
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
_ProgressBar.BeginInvoke(new Action(() => _ProgressBar.Value = i));
//Process my file here
}
}
}
Catch
{
}
}
I would always suggest reducing the number of empty lines, for readability.
I also find that the 'allman' style bracketing is easiest to debug.
But as always, each to his own.
EDIT:
After OP editted code:
Try adding:
application.doevents()
after your:
_BackgroundWorker.ReportProgress(i);
Why? The code will stay in the for loop until completes. By doing 'doevents' you are telling it to update externally, before the next loop.
Without the 'doevents' i guess you would see 0 and 100 only.
Simply do something like this with a BackgroundWorker (bgw).
private void MyMethod()
{
bgw.RunWorkerAsync(); //this calls the DoWork event
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
//Expensive task
//Calculate how far you through your task (ie has read X of Y bytes of file)
bgw.ReportProgress(myInteger);
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
Make sure you set the BackgroundWorker's "WorkerReportsProgress" property to True!

C#, Constantly monitor battery level

I am designing a program that depends on monitoring the battery level of the computer.
This is the C# code I am using:
PowerStatus pw = SystemInformation.PowerStatus;
if (pw.BatteryLifeRemaining >= 75)
{
//Do stuff here
}
My failed attempt of the while statement, it uses all the CPU which is undesirable.
int i = 1;
while (i == 1)
{
if (pw.BatteryLifeRemaining >= 75)
{
//Do stuff here
}
}
How do I monitor this constantly with an infinite loop so that when it reaches 75% it will execute some code.
Try Timer:
public class Monitoring
{
System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
public Monitoring()
{
timer1.Interval = 1000; //Period of Tick
timer1.Tick += timer1_Tick;
}
private void timer1_Tick(object sender, EventArgs e)
{
CheckBatteryStatus();
}
private void CheckBatteryStatus()
{
PowerStatus pw = SystemInformation.PowerStatus;
if (pw.BatteryLifeRemaining >= 75)
{
//Do stuff here
}
}
}
UPDATE:
There is another way to do your task complete. You can use SystemEvents.PowerModeChanged.
Call it and wait for changes, monitor the changes occured then do your stuff.
static void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e)
{
if (e.Mode == Microsoft.Win32.PowerModes.StatusChange)
{
if (pw.BatteryLifeRemaining >= 75)
{
//Do stuff here
}
}
}
While loop will cause your UI to response poor and the application will get crashed. You can solve this by using many ways. Please check out the below code snippet will help your needs.
public delegate void DoAsync();
private void button1_Click(object sender, EventArgs e)
{
DoAsync async = new DoAsync(GetBatteryDetails);
async.BeginInvoke(null, null);
}
public void GetBatteryDetails()
{
int i = 0;
PowerStatus ps = SystemInformation.PowerStatus;
while (true)
{
if (this.InvokeRequired)
this.Invoke(new Action(() => this.Text = ps.BatteryLifePercent.ToString() + i.ToString()));
else
this.Text = ps.BatteryLifePercent.ToString() + i.ToString();
i++;
}
}
BatteryChargeStatus.Text = SystemInformation.PowerStatus.BatteryChargeStatus.ToString();
BatteryFullLifetime.Text = SystemInformation.PowerStatus.BatteryFullLifetime.ToString();
BatteryLifePercent.Text = SystemInformation.PowerStatus.BatteryLifePercent.ToString();
BatteryLifeRemaining.Text = SystemInformation.PowerStatus.BatteryLifeRemaining.ToString();
PowerLineStatus.Text = SystemInformation.PowerStatus.PowerLineStatus.ToString();
If you want to perform some operation just convert these string values into the integer.

Can't seem to get progressbar to animate

OK so I've have a problem looking like this.
public Class A{
public A(){
progressBar.Style = ProgressBarStyle.Marquee;
progressBar.MarqueeAnimationSpeed = 0;
}
public void DoSomething(){
if(checkpasses){
progressBar.MarqueeAnimationSpeed = 100;
//Do something here...
progressBar.MarqueeAnimationSpeed = 0;
}
else
//Do nothing...
}
}
The problem is that my progressbar wont start moving at all. First I figured that it wont create a new thread by itself (which I find wired) so I tried creating a thread but still the same result. Nothing happens. Is it something I've forgotten?
Call
Application.EnableVisualStyles();
at the very beginning of your application.
Your "do something here" code is going to block the UI thread so you will not see the progress bar update until after the DoSomething method completes. At that time you are setting the animation speed back to 0.
Try putting your "do something here" code on a separate thread. When that thread completes set the animation speed back to 0.
Something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.MarqueeAnimationSpeed = 0;
progressBar1.Style = ProgressBarStyle.Blocks;
progressBar1.Value = progressBar1.Minimum;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
DoSomething();
}
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.MarqueeAnimationSpeed = 100;
backgroundWorker1.RunWorkerAsync();
}
private void DoSomething()
{
Thread.Sleep(2000);
}
}
I am not sure if this is the best solution, but I have it this way:
//this is the action item (button click)
private void importSFNFReportButton_Click(object sender, EventArgs e)
{ //I run
backgroundWorker6Progress.RunWorkerAsync(); //this is how I start the progress bar 'movement'
bgwImportSF.RunWorkerAsync(); //this is another task that is lauchned after the progress bar is initiated
}
This is actual background worker
private void backgroundWorker6Progress_DoWork(object sender, DoWorkEventArgs e)
{
bool cont = true;
while (cont)
{
PauseForMilliSeconds(100);
updateProgressbar1(false);
if (noTasksExistCheck())
{
updateProgressbar1(true);
cont = false;
}
}
}
this is a delegate- I call it to auto-increase the progress bar indicator
delegate void updateProgressBarStatus(bool done);
private void updateProgressbar1(bool done)
{
if (progressBar1.InvokeRequired)
{
updateProgressBarStatus del = new updateProgressBarStatus(updateProgressbar1);
progressBar1.Invoke(del, new object[] { done });
}
else
{
if (progressBar1.Value == progressBar1.Maximum)
{
progressBar1.Value = progressBar1.Minimum;
}
progressBar1.PerformStep();
if (done == true)
{
progressBar1.Value = progressBar1.Minimum;
}
}
}
I control it via the function that has to check a global varibale
noTasksExistCheck()
This is the timer pause
public static DateTime PauseForMilliSeconds(int MilliSecondsToPauseFor)
{
System.DateTime ThisMoment = System.DateTime.Now;
System.TimeSpan duration = new System.TimeSpan(0, 0, 0, 0, MilliSecondsToPauseFor);
System.DateTime AfterWards = ThisMoment.Add(duration);
while (AfterWards >= ThisMoment)
{
System.Windows.Forms.Application.DoEvents();
ThisMoment = System.DateTime.Now;
}
return System.DateTime.Now;
}
Just to complement a bit more, the solution suggested by Dave will only work if Konstantin's suggested code exists. Otherwise, one should think of manually increasing the progressbar.value in a loop by the following code within the DoWork:
BeginInvoke(new MethodInvoker( () => progressBarSave.Value += progressBarSave.Step));

Categories