I have this Form containing 4 labels. I want these labels to blink with a specified frequency, like 12.5, 10, 8 and 4 HZ. I used a Timer, but it won't work correctly, They blink with much lower frequency, I know it's because nested ifs in the freqMethod below. How should I solve this issue?
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.Timers;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
System.Timers.Timer mainTimer;
private int counter = 0;
Color lColor = Color.FromArgb(255, 192, 128);
bool buttonPressed = false;
public Form1()
{
InitializeComponent();
label1.BackColor = lColor;
label2.BackColor = lColor;
label3.BackColor = lColor;
label4.BackColor = lColor;
mainTimer = new System.Timers.Timer(1);
mainTimer.Elapsed += new ElapsedEventHandler(timerElapsed);
}
private void button2_Click(object sender, EventArgs e)
{
if (buttonPressed)
{
mainTimer.Enabled = false;
buttonPressed = !buttonPressed;
counter = 0;
}
else
{
mainTimer.Enabled = true;
buttonPressed = !buttonPressed;
counter = 0;
}
}
//Frequency Method
public void freqMethod()
{
if (counter % 80 == 0)
if (label4.backColor == lColor)
label4.backColor = Color.black;
else
label4.backColor = lColor;
if (counter % 100 == 0)
if (label3.backColor == lColor)
label3.backColor = Color.black;
else
label3.backColor = lColor;
if (counter % 125 == 0)
if (label2.backColor == lColor)
label2.backColor = Color.black;
else
label2.backColor = lColor;
if (counter % 250 == 0)
if (label1.backColor == lColor)
label1.backColor = Color.black;
else
label1.backColor = lColor;
}
private void timerElapsed(object source, ElapsedEventArgs e) {
counter++;
freqMethod();
}
}
}
Given the following values (and if you want to synchronized them using one timer) the common interval they can have is 5ms. So you need to tick the timer 5ms each and check the frequency. But take note on using the timer as explained below:
12.5hz = 80ms
10hz = 100ms
8hz = 125ms
4hz = 250ms
Remarks from MSDN using System.Timers.Timer (https://msdn.microsoft.com/en-us/library/system.timers.timer.interval(v=vs.110).aspx) See remarks section.
You use the Interval property to determine the frequency at which the
Elapsed event is fired. Because the Timer class depends on the system
clock, it has the same resolution as the system clock. This means that
the Elapsed event will fire at an interval defined by the resolution
of the system clock if the Interval property is less than the
resolution of the system clock. The following example sets the
Interval property to 5 milliseconds. When run on a Windows 7 system
whose system clock has a resolution of approximately 15 milliseconds,
the event fires approximately every 15 milliseconds rather than every
5 milliseconds
But if you can use multiple timer for each, then you can set each interval of the timer as Yeldar mentioned.
You don't need timer to iterate every second as you skip it's every n iterations while they take too much resources. You can just manipulate with Timer's Interval value to get the required frequency with adeqate performance.
For example, for frequency 8 Hz you only need timer to fire an event every 125 ms (8 times per second).
I will provide an example with a double frequency in order to make it work with intervals < 1. For example, if you set frequency to 0.5, the color will change every 2 seconds.
Example:
public Form1()
{
double frequencyInHz = 8.0; // here goes your frequency
int interval = (int)Math.Round(1000.0 / frequencyInHz); // 125ms in this case
mainTimer = new Timer(interval);
mainTimer.Elapsed += new ElapsedEventHandler(timerElapsed);
}
private void timerElapsed(object source, ElapsedEventArgs e) {
if (label2.BackColor == lColor)
label2.BackColor = Color.Black;
else
label2.BackColor = lColor;
}
If you need several labels to change their colors with different, you will need to make several timers in order to get a good performance.
Try this (in addition to the timer interval modification proposed by Joel or Yeldar) :
if (counter % 80 == 0)
{
label4.backColor = label4.backColor == lColor ? Color.black : lColor;
label4.Refresh() ;
}
Related
I'm trying to make a smooth progress bar that "loads" from 0 to 100 based on a time value (in seconds).
So far, I've got a method that sets up the progress bar's maximum value to 100 * loadTime. Additionally, I've got a timer in my form, and its increment is 10 milliseconds. The idea is that the progress bar's maximum, divided by the timer's increment, will equal the number of seconds the progress bar needs to load.
Unfortunately, there are some inconsistencies with my timer. For instance, if I set my timer's increment to 1000 milliseconds and the bar's maximum to loadTime, it will be somewhat consistent, but it doesn't do anything for the first second. It's also very jittery. At 100 milliseconds and 10 * loadTime, it's slightly more consistent, but still very jittery. 10 milliseconds seems to be the sweet spot in terms of smoothness, however, if for instance, loadTime is equal to 5, it will load the progress bar in approximately 7 or 8 seconds.
I have also tried setting the timer's increment to 1, and my bar's maximum to 1000 * loadTime, however, this just makes it slower, and results in times of 10-13 seconds, when it should be 5 for instance.
Why's this the case? Can anything be done about it?
DisplayTimeProgressBar.cs (credit to Crispy's answer)
[DesignerCategory("code")]
public class DisplayTimeProgressBar : ProgressBar
{
public DisplayTimeProgressBar()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rec = e.ClipRectangle;
rec.Width = (int)(rec.Width * ((double)Value / Maximum));
rec.Height = rec.Height;
e.Graphics.FillRectangle(Brushes.Aquamarine, 0, 0, rec.Width, rec.Height);
}
}
Form.Designer.cs
private System.Windows.Forms.Timer progressBarTimer = new System.Windows.Forms.Timer(this.components);
private DisplayTimeProgressBar displayTimeProgressBar = new DisplayTimeProgressBar();
Form.cs
private void loadBar(int timeToDisplay)
{
this.displayTimeProgressBar.Visible = true;
this.displayTimeProgressBar.Maximum = timeToDisplay * 100;
this.progressBarTimer.Start();
}
private void progressBarTimer_Tick(object sender, EventArgs e)
{
if (this.displayTimeProgressBar.Value >= this.displayTimeProgressBar.Maximum)
{
this.displayTimeProgressBar.Value = 0;
this.displayTimeProgressBar.Visible = false;
this.progressBarTimer.Stop();
}
else
{
this.displayTimeProgressBar.Value++;
}
}
The following is a visual demonstration of my issue. The parameter loadTime has been set to 5 (5 seconds). In this demo, the timer's increment is 10, and the bar's maximum is 500.
If you want a continous animation over a specifc duration, wouldn't you have to base it on frames per second (i.e. 40 fps), as well as taking into consideration the maximum width in pixels of the monitor, because the Value corresponds to the number of pixels filled. In other words, using 100 for the Maximum value doesn't make sense. So then sleep 25 millis, calculate the true elapsed time (current time minus start time), divide the elapsed time by the total duration. Now you have a percentage. Multiply that percentage by the Maximum value, and that is the current Value. Also, setting pb.Style = ProgressBarStyle.Continuous; seems to look better, but it may not apply to your situation since you are using a custom paint. Example:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form f5 = new Form();
ProgressBar pb = new ProgressBar { Dock = DockStyle.Top, Minimum = 0, Maximum = 2000 };
pb.Style = ProgressBarStyle.Continuous;
double durationSeconds = 5;
Button btnStart = new Button { Text = "Start", Dock = DockStyle.Bottom };
double fps = 40; // refreshes per second
btnStart.Click += delegate {
int maxValue = pb.Maximum;
Thread t = new Thread(() => {
DateTime utcNow = DateTime.UtcNow;
int sleepMillis = (int) (1000 / fps);
int progress = 0;
while (true) {
Thread.Sleep(sleepMillis);
DateTime utcNow2 = DateTime.UtcNow;
double elapsedSeconds = (utcNow2 - utcNow).TotalSeconds;
int newProgress = (int) Math.Round(maxValue * elapsedSeconds / durationSeconds);
if (newProgress >= maxValue) {
pb.BeginInvoke((Action) delegate {
pb.Value = maxValue;
});
break;
}
else if (newProgress > progress) {
pb.BeginInvoke((Action) delegate {
pb.Value = newProgress;
});
}
progress = newProgress;
}
});
t.IsBackground = true;
t.Start();
};
f5.Controls.Add(pb);
f5.Controls.Add(btnStart);
Application.Run(f5);
I am making my own little custom message box, essentially its just a small box showing up with a message for X amount of time, and then fades away for Y duration.
What is happening tho is that the fade away takes double as long as its supposed to and I cannot figure out why. Can someone look at my code and spot why its taking double the amount of time to fade the form away than it is expected?
//Initiate and set up the message bubble.
public static void InitiateBubble(String displayText, Double showTime = 1000, Double fadeTime = 2000) {
Bubble bubble = new Bubble(displayText);
bubble.showTime = showTime;
bubble.fadeTime = fadeTime;
bubble.Show();
bubble.showTimer = new Timer();
bubble.showTimer.Interval = (int)bubble.showTime;
bubble.showTimer.Tick += bubble.startFadeAway;
bubble.showTimer.Start();
}
//Leaves some time on screen before starting to fade away
private void startFadeAway(object sender, EventArgs e) {
showTimer.Stop();
fadeAwayTimer = new Timer();
fadeAwayTimer.Interval = 10;
fadeAwayTimer.Tick += fadeAway;
fadeAwayTimer.Start();
}
//slowly fades the contorle away until it disapears.
private void fadeAway(object sender, EventArgs e) {
double opacity = Opacity;
opacity -= (10 / fadeTime);
if (opacity < 0) {
Close();
}
else {
Opacity = opacity;
}
}
If the user sets the fade interval to 1 second (1000 milliseconds), and we set the timer interval to 1/10th of a second (100 milliseconds), then we need to fade the opacity by 10% every interval (since the interval is triggered 10 times per second). So we would set Opacity -= .1 on each iteration.
If the user sets the fade interval to 2 seconds (2000 milliseconds), and we still have the timer interval set to 1/10th of a second, then we need to fade the opacity by only 5% every interval, so we would set Opacity -= .05 on each iteration.
Seeing this relationship, we can discover that:
var amountToReduceOpacity = 1.0 / fadeTime * interval;
Note: as γηράσκω δ' αεί πολλά διδασκόμε mentioned above, the resolution on the winform timer is around 17 milliseconds, so if we set it to less than this, the fade will slow down dramatically because we will have calculated the rate for a very fast timer (meaning that we won't fade very much each iteration), but it will execute more slowly. On my machine, setting it to 50 looks just fine.
Now we can use this formula to always fade the form by the correct amount each interval. Here's a sample Form that does basically what you're doing above (note that I dropped a label and two timers on the form, and named them: lblDisplay, showTimer, and fadeTimer):
public partial class Bubble : Form
{
private readonly double amountToReduceOpacity;
private readonly int fadeInterval = 50;
// Custom form constructor takes in all three required settings
public Bubble(string displayText, int showTime, int fadeTime)
{
InitializeComponent();
lblDisplay.AutoSize = true;
lblDisplay.Text = displayText;
lblDisplay.Left = ClientRectangle.Width / 2 - lblDisplay.Width / 2;
lblDisplay.Top = ClientRectangle.Height / 2 - lblDisplay.Height / 2;
showTimer.Interval = showTime;
fadeTimer.Interval = fadeInterval;
amountToReduceOpacity = 1.0 / fadeTime * fadeInterval;
}
// The Shown event starts the first timer
private void Bubble_Shown(object sender, EventArgs e)
{
showTimer.Start();
}
// The shownTimer starts the fadeTimer
private void showTimer_Tick(object sender, EventArgs e)
{
showTimer.Stop();
BackColor = Color.Red; // Just so we see when the fade starts
fadeTimer.Start();
}
// The fade timer reduces opacity on each iteration until it's zero
private void fadeTimer_Tick(object sender, EventArgs e)
{
Opacity -= amountToReduceOpacity;
if (Opacity <= 0) Close();
}
}
Then on the client side we can just do something like:
private void button1_Click(object sender, EventArgs e)
{
Bubble bubble = new Bubble("Help me, I'm Fading!", 1000, 2000);
bubble.Show();
}
public PbsWheel(AnimatedPictureBox.AnimatedPictureBoxs[] pbs, AnimatedPictureBox.AnimatedPictureBoxs pb, int delta,Label label2)
{
for (int i = 0; i < pbs.Length; i++)
{
if (delta > 0)
{
pbs[i].AnimateRate += 1/60 * 1000;
1/60 * 1000 is 60 frames per second ?
This is how i animate the pictureBoxes the images inside. Im using timer for each picturebox:
public class AnimatedPictureBoxs : PictureBox
{
public static bool images;
List<string> imageFilenames;
Timer t = new Timer();
public AnimatedPictureBoxs()
{
images = false;
AnimateRate = 100; //It's up to you, the smaller, the faster.
t.Tick += Tick_Animate;
}
public int AnimateRate
{
get { return t.Interval; }
set { t.Interval = value; }
}
public void Animate(List<string> imageFilenames)
{
this.imageFilenames = imageFilenames;
t.Start();
}
public void StopAnimate()
{
t.Stop();
i = 0;
}
int i;
private void Tick_Animate(object sender, EventArgs e)
{
if (images == true)
{
imageFilenames = null;
}
if (imageFilenames == null)
{
return;
}
else
{
try
{
if (i >= imageFilenames.Count)
{
i = 0;
}
else
{
Load(imageFilenames[i]);
i = (i + 1) % imageFilenames.Count;
}
}
catch (Exception err)
{
}
}
}
The rate is set to 100 what i want to do is to display and when i move the mouse wheel up down to change the speed of the images animate by frames per second.
pbs is array of pictureBoxes.
pbs[i].AnimateRate += 1/60 * 1000;
Now, AnimateRate is an integer property. It is very badly named. It is not a rate. It is a timer interval. In mathematical terms it is a period. Naming it rate makes it sound as though it will be a rate, or a frequency.
The mathematical relationship between period T and frequency f is:
T = 1/f
So, here's what you should do:
Rename the property as AnimationInterval.
When you need to convert a frequency (i.e. frame rate) to an interval use the formula above.
Note that you need to account for the fact that your frequencies are measured in frames per second, but your intervals are measured in milli-seconds. So your code should be:
pbs[i].AnimationInterval += 1000/60;
That looks very similar to what you had but there is a subtle difference. In mathematics, the formulae are identical. But in C#, the behaviour of the / operator depends on the types of its operands. You supply two integers and so / is integer division. And the result of 1/60 is zero. So your code does not modify the property.
I do think that you will need to modify your logic a little. As it stands, your raw data is an interval. But actually what you wish to control if frame rate. So I believe that you should maintain a variable that holds the frame rate. If you want to modify it, then make the modifications to the frame rate variable. And then set the interval like this:
pbs[i].AnimationInterval = 1000/frameRate;
I'm building an auto clicker application that makes the mouse click at a set interval of time.
Notice the interval configuration area. I have tried to write some logic that automatically simplifies the amount of time entered. The minutes box goes up to 60, the seconds box goes up to 60, and the milliseconds box goes up to 1000. I have a class set up to handle that logic, but it's probably not the right way to do that (I'm still new at programming).
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 AutoClicker {
public static class TotalTime {
public static int Interval(NumericUpDown m_, NumericUpDown s_, NumericUpDown ms_) {
int m = (int)m_.Value;
int s = (int)s_.Value;
int ms = (int)ms_.Value;
int total = 0;
total = total + (m * 60000);
total = total + (s * 1000);
total = total + ms;
return total;
}
public static void ChangeLogic(NumericUpDown m_, NumericUpDown s_, NumericUpDown ms_) {
int interval = Interval(m_, s_, ms_);
if (ms_.Value == 1000)
{
ms_.Value = 500;
s_.UpButton();
ms_.Value = 0;
}
if (s_.Value == 60 && m_.Value < 60)
{
if (ms_.Value == 0)
{
ms_.Value = 1;
s_.Value = 0;
m_.UpButton();
ms_.Value = 0;
}
else
{
s_.Value = 0;
m_.UpButton();
if (ms_.Value == 1)
ms_.DownButton();
}
}
if (ms_.Value == -1)
{
ms_.Value = 999;
s_.DownButton();
}
if (s_.Value == -1 & m_.Value > 0)
{
s_.Value = 59;
m_.DownButton();
if (ms_.Value == 1)
ms_.DownButton();
}
}
}
}
The ChangeLogic method is called each time the value in any of the boxes is updated.
There's lots of bugs in the selection logic. For example, when the up button for seconds is held down, the program crashes. If seconds = 59 and minutes = 0, and the seconds up button is pressed, the warning "you can't have a time less than 250 milliseconds" shows up and minutes is adjusted to 2.
I'm really confused. Can anyone help me? Thanks!
So, it looks like your intent is to have it so that when you up the milliseconds it then rolls up to the seconds once it hits 1000, then when your seconds hit 60 it rolls up to your minutes, etc etc.
Here is how I would accomplish this. First set all of your NUDs to max value of 99999. Then use code that looks something like this:
(Note the use of the intervalUpdating variable. This will make it so that you can hold that up button and while the intervalChanged() method is executing it wont try to execute it again.)
private void nudSeconds_ValueChanged(object sender, EventArgs e)
{
intervalChanged();
}
private void nudMilliseconds_ValueChanged(object sender, EventArgs e)
{
intervalChanged();
}
private bool intervalUpdating = false;
private void intervalChanged()
{
if (intervalUpdating)
{
return;
}
intervalUpdating = true;
if (nudMilliseconds.Value >= 1000)
{
var val = (int)nudMilliseconds.Value / 1000;
nudSeconds.Value += val;
nudMilliseconds.Value = (nudMilliseconds.Value - (val * 1000));
}
if (nudSeconds.Value >= 60)
{
var val = (int)nudSeconds.Value / 60;
nudMinutes.Value += val;
nudSeconds.Value = (nudSeconds.Value - (val * 60));
}
intervalUpdating = false;
}
I have the code below which is an implementation of a marquee label. I'd like the marquee label to run at the most efficient rate possible on the computer while maintaining a constant speed across different computers. I'd like the amount of "frames" that are skipped to be minimized so that the program takes full advantage of the computer it's being run on. But at the same time I don't necessarily want to consume 100% of the CPU (are those statements contradictory? I'm not sure).
Currently I'm sleeping 10 milliseconds for each iteration of the animation loop. This feels wasteful to me and it seems like it might slow down the animation on slower computers that might need those extra 10 milliseconds. I'm not sure what the best value is to use in the sleep method or even if I should sleep at all. I've read some about Sleep(0) and Sleep(1) and Yield and SpinWait, but I can't make sense of it all.
Also is it a bad thing to call Invalidate too much? Can I overload it by calling it too much?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Diagnostics;
using Timer = System.Timers.Timer;
using System.Threading;
namespace Scoreboard
{
public class MarqueeLabel : Label
{
private float _marqueeOffset;
private float _marqueeMeasurement;
public MarqueeLabel()
{
UseCompatibleTextRendering = true;
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
Thread t = new Thread(ThreadRun);
t.IsBackground = true;
t.Start();
}
public void ThreadRun()
{
long time = DateTime.Now.Ticks;
long diff = 0;
while (true)
{
float step = -((float)diff / (20 * TimeSpan.TicksPerMillisecond)); //change "frame" based on the elapsed time
_marqueeOffset = _marqueeOffset >= -_marqueeMeasurement ? _marqueeOffset + step : Width;
Invalidate();
Thread.Sleep(10); // how long should i wait here?
long temp = DateTime.Now.Ticks;
diff = temp - time;
time = temp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
StringFormat stringFormat = new StringFormat();
stringFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
stringFormat.Trimming = StringTrimming.None;
stringFormat.Alignment = StringAlignment.Near;
Rectangle rect = ClientRectangle;
rect.X += (int)_marqueeOffset;
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), rect, stringFormat);
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
MeasureText();
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
MeasureText();
}
void MeasureText()
{
_marqueeMeasurement = CreateGraphics().MeasureString(Text, Font).Width;
}
}
}