I have a form that displays queue of messages and number this messages can be changed. Really I want to blink label (queue length) when the number of messages were increased to improve form usability.
Should I implement custom control and use additional thread or timer to change color of label? Has anybody implemented so functionality? What is the best solution (less resources and less performance degradation) to implement so behaviour?
SOLUTION:
Form's component with timer that can restrict number of animations per second and implement fade out effect to external control background color.
The following is blinking using async and await
private async void Blink(){
while (true){
await Task.Delay(500);
label1.BackColor = label1.BackColor == Color.Red ? Color.Green : Color.Red;
}
}
I know this is a really old post, but anyone looking for something a little more versatile than the Boolean solutions posted may get some use out of the following:
using System.Diagnostics;
using System.Threading.Tasks;
private async void SoftBlink(Control ctrl, Color c1, Color c2, short CycleTime_ms, bool BkClr)
{
var sw = new Stopwatch(); sw.Start();
short halfCycle = (short)Math.Round(CycleTime_ms * 0.5);
while (true)
{
await Task.Delay(1);
var n = sw.ElapsedMilliseconds % CycleTime_ms;
var per = (double)Math.Abs(n - halfCycle) / halfCycle;
var red = (short)Math.Round((c2.R - c1.R) * per) + c1.R;
var grn = (short)Math.Round((c2.G - c1.G) * per) + c1.G;
var blw = (short)Math.Round((c2.B - c1.B) * per) + c1.B;
var clr = Color.FromArgb(red, grn, blw);
if (BkClr) ctrl.BackColor = clr; else ctrl.ForeColor = clr;
}
}
Which you can call like such:
SoftBlink(lblWarning, Color.FromArgb(30, 30, 30), Color.Red,2000,false);
SoftBlink(lblSoftBlink, Color.FromArgb(30, 30, 30), Color.Green, 2000,true);
Timer timer = new Timer();
timer.Interval = 500;
timer.Enabled = false;
timer.Start();
if( messagesNum > oldMessagesNum)
timer.Tick += new EventHandler( timer_Tick );
else
timer.Tick -= timer_Tick;
void timer_Tick( object sender, EventArgs e )
{
if(messageLabel.BackColor == Color.Black)
messageLabel.BackColor = Color.Red;
else
messageLabel.BackColor = Color.Black;
}
Here is a pretty simple implementation that would work inside your form. You could also create a custom control with the same code and just throw the Timer.Start() into a method for that control.
Create your own UserControl for this, one that inherits from Label instead of from Control directly. Add a StartBlinking method, in which you start a Timer object whose tick event alters the style of the label (changing the BackgroundColor and ForegroundColor properties each time to create the blink effect).
You could also add a StopBlinking method to turn it off, or you could have your Timer stop itself after 5 seconds, perhaps.
You can create a custom component and events to start blinking --which I think is a good solution. The Blinking you can implement with a timer.
Can you use an animated .gif instead (perhaps as the background of the number)? it would make it look like old school web pages, but it might work.
You can use Timer class here.
Here what I have implemented.
Label color blinking on Button_click Event.
//click event on the button to change the color of the label
public void buttonColor_Click(object sender, EventArgs e)
{
Timer timer = new Timer();
timer.Interval = 500;// Timer with 500 milliseconds
timer.Enabled = false;
timer.Start();
timer.Tick += new EventHandler(timer_Tick);
}
void timer_Tick(object sender, EventArgs e)
{
//label text changes from 'Not Connected' to 'Verifying'
if (labelFirst.BackColor == Color.Red)
{
labelFirst.BackColor = Color.Green;
labelFirst.Text = "Verifying";
}
//label text changes from 'Verifying' to 'Connected'
else if (labelFirst.BackColor == Color.Green)
{
labelFirst.BackColor = Color.Green;
labelFirst.Text = "Connected";
}
//initial Condition (will execute)
else
{
labelFirst.BackColor = Color.Red;
labelFirst.Text = "Not Connected";
}
}
this is how i ended up doing it
public partial class MemberDisplay : Form
{
public string Input;
public int MASS_STOP = 1;
public MemberDisplay(string msg)
{
InitializeComponent();
State_Entry();
Input = msg;
}
public void State_Entry()
{
this.SpecialFocus.Select();
this.lbl_TimerTest.Hide();
}
private async void RunBlinkyTest(string msg)
{
while (msg == "GO" && (MASS_STOP == 0))
{
await Task.Delay(500);
lbl_TimerTest.ForeColor =
lbl_TimerTest.ForeColor == Color.Red ?
Color.Black :
Color.Red;
if (msg == "STOP" && (MASS_STOP == 1)) { return; }
}
}
private void btn_TimeTest_Click(object sender, EventArgs e)
{
if (btn_TimeTest.Text == "GO")
{
this.lbl_TimerTest.Show();
MASS_STOP = 0;
RunBlinkyTest("GO");
btn_TimeTest.Text = "STOP";
return;
}
if (btn_TimeTest.Text == "STOP")
{
MASS_STOP = 1;
RunBlinkyTest("STOP");
this.lbl_TimerTest.ForeColor = Color.Black;
this.lbl_TimerTest.Hide();
btn_TimeTest.Text = "GO";
return;
}
}
}
Related
I have a form that displays queue of messages and number this messages can be changed. Really I want to blink label (queue length) when the number of messages were increased to improve form usability.
Should I implement custom control and use additional thread or timer to change color of label? Has anybody implemented so functionality? What is the best solution (less resources and less performance degradation) to implement so behaviour?
SOLUTION:
Form's component with timer that can restrict number of animations per second and implement fade out effect to external control background color.
The following is blinking using async and await
private async void Blink(){
while (true){
await Task.Delay(500);
label1.BackColor = label1.BackColor == Color.Red ? Color.Green : Color.Red;
}
}
I know this is a really old post, but anyone looking for something a little more versatile than the Boolean solutions posted may get some use out of the following:
using System.Diagnostics;
using System.Threading.Tasks;
private async void SoftBlink(Control ctrl, Color c1, Color c2, short CycleTime_ms, bool BkClr)
{
var sw = new Stopwatch(); sw.Start();
short halfCycle = (short)Math.Round(CycleTime_ms * 0.5);
while (true)
{
await Task.Delay(1);
var n = sw.ElapsedMilliseconds % CycleTime_ms;
var per = (double)Math.Abs(n - halfCycle) / halfCycle;
var red = (short)Math.Round((c2.R - c1.R) * per) + c1.R;
var grn = (short)Math.Round((c2.G - c1.G) * per) + c1.G;
var blw = (short)Math.Round((c2.B - c1.B) * per) + c1.B;
var clr = Color.FromArgb(red, grn, blw);
if (BkClr) ctrl.BackColor = clr; else ctrl.ForeColor = clr;
}
}
Which you can call like such:
SoftBlink(lblWarning, Color.FromArgb(30, 30, 30), Color.Red,2000,false);
SoftBlink(lblSoftBlink, Color.FromArgb(30, 30, 30), Color.Green, 2000,true);
Timer timer = new Timer();
timer.Interval = 500;
timer.Enabled = false;
timer.Start();
if( messagesNum > oldMessagesNum)
timer.Tick += new EventHandler( timer_Tick );
else
timer.Tick -= timer_Tick;
void timer_Tick( object sender, EventArgs e )
{
if(messageLabel.BackColor == Color.Black)
messageLabel.BackColor = Color.Red;
else
messageLabel.BackColor = Color.Black;
}
Here is a pretty simple implementation that would work inside your form. You could also create a custom control with the same code and just throw the Timer.Start() into a method for that control.
Create your own UserControl for this, one that inherits from Label instead of from Control directly. Add a StartBlinking method, in which you start a Timer object whose tick event alters the style of the label (changing the BackgroundColor and ForegroundColor properties each time to create the blink effect).
You could also add a StopBlinking method to turn it off, or you could have your Timer stop itself after 5 seconds, perhaps.
You can create a custom component and events to start blinking --which I think is a good solution. The Blinking you can implement with a timer.
Can you use an animated .gif instead (perhaps as the background of the number)? it would make it look like old school web pages, but it might work.
You can use Timer class here.
Here what I have implemented.
Label color blinking on Button_click Event.
//click event on the button to change the color of the label
public void buttonColor_Click(object sender, EventArgs e)
{
Timer timer = new Timer();
timer.Interval = 500;// Timer with 500 milliseconds
timer.Enabled = false;
timer.Start();
timer.Tick += new EventHandler(timer_Tick);
}
void timer_Tick(object sender, EventArgs e)
{
//label text changes from 'Not Connected' to 'Verifying'
if (labelFirst.BackColor == Color.Red)
{
labelFirst.BackColor = Color.Green;
labelFirst.Text = "Verifying";
}
//label text changes from 'Verifying' to 'Connected'
else if (labelFirst.BackColor == Color.Green)
{
labelFirst.BackColor = Color.Green;
labelFirst.Text = "Connected";
}
//initial Condition (will execute)
else
{
labelFirst.BackColor = Color.Red;
labelFirst.Text = "Not Connected";
}
}
this is how i ended up doing it
public partial class MemberDisplay : Form
{
public string Input;
public int MASS_STOP = 1;
public MemberDisplay(string msg)
{
InitializeComponent();
State_Entry();
Input = msg;
}
public void State_Entry()
{
this.SpecialFocus.Select();
this.lbl_TimerTest.Hide();
}
private async void RunBlinkyTest(string msg)
{
while (msg == "GO" && (MASS_STOP == 0))
{
await Task.Delay(500);
lbl_TimerTest.ForeColor =
lbl_TimerTest.ForeColor == Color.Red ?
Color.Black :
Color.Red;
if (msg == "STOP" && (MASS_STOP == 1)) { return; }
}
}
private void btn_TimeTest_Click(object sender, EventArgs e)
{
if (btn_TimeTest.Text == "GO")
{
this.lbl_TimerTest.Show();
MASS_STOP = 0;
RunBlinkyTest("GO");
btn_TimeTest.Text = "STOP";
return;
}
if (btn_TimeTest.Text == "STOP")
{
MASS_STOP = 1;
RunBlinkyTest("STOP");
this.lbl_TimerTest.ForeColor = Color.Black;
this.lbl_TimerTest.Hide();
btn_TimeTest.Text = "GO";
return;
}
}
}
I saw this cool thing about having a RichTextBox like a "progress bar" if we can call it like that from another window application and I was trying to do the same.
I was looking at the methods and I couldn't apply any of them on what I'm looking for, I tried to see if there was a similar question but I didn't get so lucky.
How can I accomplish the same result?
Looks like a Richtextbox to me
Based on what Jimi told me I will to explain what I need. The string inside label rappresents a timer which has to change the color in red when it reaches the end, before of that, like at 10 minutes I want it to be Yellow, using that like an alert.
The way the method is wroten doesn't let me to choose which color I want. After that, when the timer is stopped by a button, I want the label to redraws itself and makes it blank without having any kind of colors, looking like a "textbox".
This is a label used as a ProgressBar.
Just an example (quite raw, what I could do with the time I had), but it shows how you can paint the surface of a Control that provides a Paint() event.
It uses a Timer class to increase a value and generates a progress bar effect by calling the Label.Invalidate() method, which raises the Label's Paint event, executing whatever code you have in the label1_Paint() handler.
If you want to test it, paste this code inside a Form which contains a Button (button1) to start the Timer and a Label (label1) that generates the graphic effect.
Then assign the two events - Click() to the Button and Paint() to the Label.
This is how it looks like:
Timer timer;
private bool TimerStarted = false;
private float ProgressMaxValue = 100;
private float Progress = 0;
private int seconds = 0;
private int cents = 0;
private void button1_Click(object sender, EventArgs e)
{
if (TimerStarted) { TimerStop(); return; }
timer = new Timer();
timer.Interval = 20;
Progress = 0;
seconds = 0;
cents = 0;
timer.Tick += (s, ev) => {
++Progress;
if (Progress > ProgressMaxValue) { TimerStop(); return; }
cents += (timer.Interval / 5);
if (cents > 99) { cents = 0; ++seconds; }
this.label1.Invalidate();
};
TimerStarted = true;
timer.Start();
}
private void TimerStop()
{
timer.Stop();
timer.Dispose();
TimerStarted = false;
}
private void label1_Paint(object sender, PaintEventArgs e)
{
StringFormat format = new StringFormat() {
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
e.Graphics.Clear(this.label1.BackColor);
Rectangle rect = label1.ClientRectangle;
rect.Inflate(-1, -1);
e.Graphics.DrawRectangle(Pens.LimeGreen, rect);
RectangleF ProgressBar = new RectangleF(
new PointF(3, 3),
new SizeF((((float)rect.Width - 3) / ProgressMaxValue) * Progress, rect.Height - 4));
e.Graphics.FillRectangle(Brushes.YellowGreen, ProgressBar);
e.Graphics.DrawString($"0.{seconds.ToString("D2")}.{cents.ToString("D2")}", label1.Font, Brushes.White, rect, format);
}
This question already has answers here:
Await/async doesn't work as expected
(3 answers)
Closed 5 years ago.
I have a program that checks what is the user reaction time based on speed he clicks on the button when it changes to green. I would like the function that is working after clicking the button to run in the background.
I though by using Task it will make it asynchronously but it seems to not be the case. I understand that there is an await and probably I should make it somehow return to place it was called but I have a problem finding the solution.
My code so far looks like this
public partial class Reaction : Form
{
Stopwatch timer;
bool start = false;
public Reaction()
{
InitializeComponent();
button1.Text = "START";
button1.BackColor = Color.Red;
}
public async Task Test()
{
if (start)
{
timer.Stop();
TimeSpan timespan = timer.Elapsed;
string timeString = String.Format("{0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10);
MessageBox.Show(timeString);
button1.BackColor = Color.Red;
button1.Text = "START";
start = false;
}
else
{
Random rnd = new Random();
int number = rnd.Next(1, 10000);
System.Threading.Thread.Sleep(3000 + number);
timer = Stopwatch.StartNew();
button1.BackColor = Color.Green;
button1.Text = "CLICK";
start = true;
}
}
private async void button1_Click(object sender, EventArgs e)
{
button1.BackColor = Color.Red;
button1.Text = "Dont click";
await Test();
}
}
Based on the comments I edited the post to reflect more closely what you were asking for. Remove the async and await in the button's click event and replace the System.Threading.Thread.Sleep(... with await Task.Delay(... This way you will not have to use BeginInvoke on button1 and the only thing on a separate thread will be the Task.Delay(...
The UI will become responsive during the Task.Delay(... and then resume where it left off when the delay is finished.
public partial class Reaction : Form
{
Stopwatch timer;
bool start = false;
public Reaction()
{
...
}
public async Task Test()
{
if (start)
{
...
}
else
{
Random rnd = new Random();
int number = rnd.Next(1, 10000);
// Replace Thread.Sleep with Task.Delay
await Task.Delay(3000 + number);
timer = Stopwatch.StartNew();
button1.BackColor = Color.Green;
button1.Text = "CLICK";
start = true;
}
}
// Make button1_Click not async and remove await
private void button1_Click(object sender, EventArgs e)
{
button1.BackColor = Color.Red;
button1.Text = "Dont click";
Test();
}
}
What I'm trying to do is start a function, then change a bool to false, wait a second and turn it to true again. However I'd like to do it without the function having to wait, how do I do this?
I can only use Visual C# 2010 Express.
This is the problematic code. I am trying receive user input (right arrow for example) and move accordingly, but not allow further input while the character is moving.
x = Test.Location.X;
y = Test.Location.Y;
if (direction == "right")
{
for (int i = 0; i < 32; i++)
{
x++;
Test.Location = new Point(x, y);
Thread.Sleep(31);
}
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
int xmax = Screen.PrimaryScreen.Bounds.Width - 32;
int ymax = Screen.PrimaryScreen.Bounds.Height - 32;
if (e.KeyCode == Keys.Right && x < xmax) direction = "right";
else if (e.KeyCode == Keys.Left && x > 0) direction = "left";
else if (e.KeyCode == Keys.Up && y > 0) direction = "up";
else if (e.KeyCode == Keys.Down && y < ymax) direction = "down";
if (moveAllowed)
{
moveAllowed = false;
Movement();
}
moveAllowed = true;
}
Use Task.Delay:
Task.Delay(1000).ContinueWith((t) => Console.WriteLine("I'm done"));
or
await Task.Delay(1000);
Console.WriteLine("I'm done");
For the older frameworks you can use the following:
var timer = new System.Timers.Timer(1000);
timer.Elapsed += delegate { Console.WriteLine("I'm done"); };
timer.AutoReset = false;
timer.Start();
Example according to the description in the question:
class SimpleClass
{
public bool Flag { get; set; }
public void function()
{
Flag = false;
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (src, args) => { Flag = true; Console.WriteLine("I'm done"); };
timer.AutoReset = false;
timer.Start();
}
}
I realize an answer has already been accepted, and I do like ixSci's answer where he recommends the use of a Timer object to accomplish OP's goal.
However, using System.Timers.Timer specifically introduces threading considerations. And to ensure correctness in this case, more code is required to properly synchronize the boolean flag value. Basically, anywhere where the flag is read or written, the code region would need to have a lock statement defined around it.
It would have to look something like this:
private final object flagLock = new object();
private bool moveAllowed = true;
private System.Timers.Timer timer = new System.Timers.Timer();
public Form1()
{
this.timer.Interval = 1000;
this.timer.AutoReset = false;
this.timer.Elapsed += (s, e) =>
{
// this DOES NOT run on the UI thread, so locking IS necessary to ensure correct behavior.
this.timer.Stop();
lock (this.flagLock) {
this.moveAllowed = true;
}
};
}
// The code in this event handler runs on the UI thread.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
// Locking is necessary here too.
lock (this.flagLock) {
if (this.moveAllowed)
{
this.moveAllowed = false;
Movement();
this.timer.Start(); // wait 1 second to reset this.moveAllowed to true.
}
}
}
Alternatively, to avoid having to think about threads, perhaps OP could consider using a different flavor of the Timer class. Namely: System.Windows.Forms.Timer. This way, the boolean flag will always be read/written on the UI thread, and no extra locking of any sort is required to ensure correctness.
In this case, the code would look something like this:
private bool moveAllowed = true;
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
public Form1()
{
this.timer.Interval = 1000;
this.timer.Tick += (s, e) =>
{
// this runs on the UI thread, so no locking necessary.
this.timer.Stop(); // this call is necessary, because unlike System.Timers.Timer, there is no AutoReset property to do it automatically.
this.moveAllowed = true;
};
}
// The code in this event handler runs on the UI thread.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (this.moveAllowed)
{
this.moveAllowed = false;
Movement();
this.timer.Start(); // wait 1 second to reset this.moveAllowed to true.
}
}
I'm sending to my method different images and I want insert some effect to this change.
How can I fade in and fade out images?
private void ShowImage(Image image, ImageLayout imageLayout, int numberOfSeconds)
{
try
{
if (this.image_timer != null)
this.KillImageTimer();
this.customer_form.DisplayImage(image, imageLayout);
this.image_timer = new Timer();
this.image_timer.Tick += (object s, EventArgs a) => NextImage();
this.image_timer.Interval = numberOfSeconds* 1000;
this.image_timer.Start();
}
catch
{
//Do nothing
}
public void DisplayImage(Image image, ImageLayout imageLayout)
{
panel1.BackgroundImage = image;
panel1.BackgroundImageLayout = imageLayout;
}
There are no built-in fading transitions in Winforms.
So you will need to write one yourself.
The simplest one I can think of uses a second Panel, that is layered upon the first one and in fact needs to be inside the first Panel or else the transparency effect won't work..
Here is the setup, using two Panels:
public Form1()
{
InitializeComponent();
pan_image.BackgroundImage = someImage;
pan_layer.Parent = pan_image;
pan_layer.BackColor = pan_image.BackColor;
pan_layer.Size = pan_image.Size;
pan_layer.Location = Point.Empty;
}
For the fading animation I use a Timer. This is a quick code example:
Timer timer1 = new Timer();
int counter = 0;
int dir = 1; // direction 1 = fade-in..
int secondsToWait = 5;
int speed1 = 25; // tick speed ms
int speed2 = 4; // alpha (0-255) change speed
void timer1_Tick(object sender, EventArgs e)
{
// we have just waited and now we fade-out:
if (dir == 0)
{
timer1.Stop();
dir = -speed2;
counter = 254;
timer1.Interval = speed2;
timer1.Start();
}
// the next alpha value:
int alpha = Math.Min(Math.Max(0, counter+= dir), 255);
button1.Text = dir > 0 ? "Fade In" : "Fade Out";
// fully faded-in: set up the long wait:
if (counter >= 255)
{
timer1.Stop();
button1.Text = "Wait";
timer1.Interval = secondsToWait * 1000;
dir = 0;
timer1.Start();
}
// fully faded-out: try to load a new image and set direction to fade-in or stop
else if (counter <= 0)
{
if ( !changeImage() )
{
timer1.Stop();
button1.Text = "Done";
}
dir = speed2;
}
// create the new, semi-transparent color:
Color col = Color.FromArgb(255 - alpha, pan_image.BackColor);
// display the layer:
pan_layer.BackColor = col;
pan_layer.Refresh();
}
I start it in a Button, on which I also show the current state:
private void button1_Click(object sender, EventArgs e)
{
dir = speed2;
timer1.Tick += timer1_Tick;
timer1.Interval = speed1;
timer1.Start();
}
As you can see I use two speeds you can set: One to control the speed of the Timer and one to control the steps by which the transparency changes on each Tick.
The effect is created by simply changing the Color from the BackgroundColor of the image Panel to fully transparent and back, waiting in between for a specified number of seconds.
And the end of the effect I call a function changeImage() to change the images. If this function returns false the Timer is stopped for good..
I'm pretty sure this could be written in a cleaner and more elegant way, but as it is it seems to work..
Update
for flicker-free display use a double-buffered control, like this Panel subclass:
class DrawPanel : Panel
{
public DrawPanel() { DoubleBuffered = true; }
}
Here is a sample implementation for changeImage:
bool changeImage()
{
if (pan_image.BackgroundImage != null)
{
var img = pan_image.BackgroundImage;
pan_image.BackgroundImage = null;
img.Dispose();
}
pan_image.BackgroundImage = Image.FromFile(imageFiles[index++]);
return index < imageFiles.Count;
}
It assumes two class level variables: a List<string> imageFiles filled with file names of images for a slide-show and an int index = 0.