Thread sleep issue
Hi,
Why do I only see appearing a red rectangle after 1 sec with the following code, and not a blue one and after a sec a red.
Just used a new forms app with a panel control added and the following code:
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics G = e.Graphics;
Rectangle R = new Rectangle(10, 10, 50, 50);
G.FillRectangle(new SolidBrush(Color.Blue), R);
Thread.Sleep(1000);
G.FillRectangle(new SolidBrush(Color.Red), R);
}
}
}
ddanbe
Senior Poster
3,829 posts since Oct 2008
Reputation Points: 2,070
Solved Threads: 661
You should invalidate the region of the form being drawn (your panel) and let windows handle the refresh. It is slightly more expensive to cause a redraw of the entire form or forcing a refresh. The operating system does the minimal amount of repainting.
It really shouldn't matter for something this small but it is "best practices". Either way Diamonddrake hit the nail on the head :)
Another alternative would be to use another thread that oscillates the color every second so you didn't put the drawing thread to sleep. That is probably the ideal mechanism of swapping out colors.
sknake
Industrious Poster
4,954 posts since Feb 2009
Reputation Points: 1,764
Solved Threads: 735
He guys, thanks.
Your replies are much appreciated!
But I still have a problem.
If I use DiamondDrake's code, it works but now I have flashing blue red rectangles one sec after the other.
This is of course caused by the last Refresh, can this be avoided?
If I leave it out I get a busy cursor and a form not responding message if I click on the form.
ddanbe
Senior Poster
3,829 posts since Oct 2008
Reputation Points: 2,070
Solved Threads: 661
@DiamondDrake: Well, this IS the intended use;)
I saw a java applet once with a little square guessing game. Just for fun I like to redo that in C#.
You have e.g. 16 squares 4x4 arranged as a grid in a bigger square and let the user guess how many squares he sees. After 3 tries, show the solution by slowly moving a colored square over all the possibilities.( in case you like to know, in the 4x4 case you can count 30 squares) So I have to draw a filled square, redraw it after sometime in the background color and draw the next square and so on. My code was just a test to see if I was on the right track, but that don't seems to be the case for the moment:S
Still have to try out your solution with the bool. Keep you informed.
BTW: Congrats with your article on CodeProject:)
ddanbe
Senior Poster
3,829 posts since Oct 2008
Reputation Points: 2,070
Solved Threads: 661
Ooh where is the codeproject link? I had started an application for code project some time back but I never finished it. You might be my inspiration to complete it :)
sknake
Industrious Poster
4,954 posts since Feb 2009
Reputation Points: 1,764
Solved Threads: 735
ddanbe
Senior Poster
3,829 posts since Oct 2008
Reputation Points: 2,070
Solved Threads: 661
Woohoo, this might be my first post where I used drawing properly :)
I took a stab at a blinking panel:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Threading.Timer;
namespace daniweb
{
public sealed class BlinkingPanel : Panel, ISupportInitialize
{
private BlinkingPanelSetup _blinkOptions;
private Timer timer;
private int paintIdx;
private bool loading;
public BlinkingPanelSetup BlinkOptions
{
get { return _blinkOptions; }
set
{
if (value == null)
return;
_blinkOptions = value;
}
}
public BlinkingPanel()
: base()
{
_blinkOptions = new BlinkingPanelSetup(this);
timer = new Timer(new TimerCallback(PaintCallback));
this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint, true);
paintIdx = default(int);
}
private void PaintCallback(Object stateInfo)
{
if (DesignMode)
return;
if ((this.BlinkOptions.Colors == null) || (this.BlinkOptions.Colors.Length <= 1))
return;
using (Graphics g = this.CreateGraphics())
{
using (SolidBrush brush = new SolidBrush(this.BlinkOptions.Colors[paintIdx]))
{
g.FillRectangle(brush, this.BlinkOptions.Rectangle);
}
}
paintIdx++;
if (paintIdx >= this.BlinkOptions.Colors.Length)
paintIdx = 0;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
internal void ResetTimer()
{
if (loading)
return;
if (this.BlinkOptions.Enabled)
{
timer.Change(0, this.BlinkOptions.Interval);
}
else
{
timer.Change(Timeout.Infinite, Timeout.Infinite);
this.Invalidate(false);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
}
base.Dispose(disposing);
}
#region ISupportInitialize Members
void ISupportInitialize.BeginInit()
{
loading = true;
}
void ISupportInitialize.EndInit()
{
loading = false;
ResetTimer();
}
#endregion
}
public sealed class BlinkingPanelSetup
{
private bool _enabled;
private Color[] _colors;
private int _interval;
private Rectangle _rectangle;
private BlinkingPanel parent;
/// <summary>
/// Colors to cycle through
/// </summary>
public Color[] Colors
{
get
{
if (_colors == null)
_colors = new Color[0];
return _colors;
}
set
{
lock (this)
{
if (value == null)
_colors = new Color[0];
else
_colors = value;
}
}
}
/// <summary>
/// Indicates if blinking is enabled
/// </summary>
public bool Enabled
{
get { return _enabled; }
set
{
lock (this)
{
if (value != _enabled)
{
_enabled = value;
parent.ResetTimer();
}
}
}
}
/// <summary>
/// Blinking interval in milliseconds
/// </summary>
public int Interval
{
get { return _interval; }
set
{
lock (this)
{
if (value <= 0)
throw new ArgumentOutOfRangeException("Interval", value, "Interval must be > 0");
if (value != _interval)
{
_interval = value;
parent.ResetTimer();
}
}
}
}
/// <summary>
/// Region to paint with the blink colors
/// </summary>
public Rectangle Rectangle
{
get { return _rectangle; }
set
{
lock (this)
{
_rectangle = value;
}
}
}
private BlinkingPanelSetup()
{
//Default options
_enabled = false;
_interval = 1000;
_colors = new Color[0];
}
internal BlinkingPanelSetup(BlinkingPanel Parent)
{
this.parent = Parent;
}
}
}
Calling it:
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;
namespace daniweb
{
public partial class frmDanny2 : Form
{
public frmDanny2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
((ISupportInitialize)panel1).BeginInit();
panel1.BlinkOptions.Colors = new Color[] { Color.Red, Color.Blue, Color.Orange, Color.DarkGreen };
panel1.BlinkOptions.Enabled = true;
panel1.BlinkOptions.Interval = 1000;
panel1.BlinkOptions.Rectangle = new Rectangle(10, 10, 50, 50);
((ISupportInitialize)panel1).EndInit();
}
private void button2_Click(object sender, EventArgs e)
{
panel1.BlinkOptions.Enabled = false;
}
}
}
The ISupportInitialize just prevents the parent being notified multiple times when you are initially setting all of the values. You will see in the designer file generated by visual studio that ISupportInitialize is used heavily.
sknake
Industrious Poster
4,954 posts since Feb 2009
Reputation Points: 1,764
Solved Threads: 735
The master at work once again!
Thanks Scott. One thing I don't get : You override the OnPaint method in your BlinkingPanel class, just to call it's base implementation, why is that?
ddanbe
Senior Poster
3,829 posts since Oct 2008
Reputation Points: 2,070
Solved Threads: 661
Because I changed my mind about how I was going to paint it and forgot to delete the override. I was originally going to override the OnPaint to draw the rectangle but read that you could double buffer the control and paint in another method -- without having to override the onpaint. I just forgot to clean and tidy up after I had it working.
sknake
Industrious Poster
4,954 posts since Feb 2009
Reputation Points: 1,764
Solved Threads: 735
LOL
Could be you left it in just to see how awake I was:D
Anyway, thanks again Scott and Diamonddrake for your answers and support.
ddanbe
Senior Poster
3,829 posts since Oct 2008
Reputation Points: 2,070
Solved Threads: 661