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);
        }
    }
}

Recommended Answers

All 13 Replies

that my friend is because you are sleeping the thread before the paint event is finished, this means windows is never told by your program that it wants be be repainted, and since nothing you are doing outside of your program is encouraging windows to repaint your program, it just draws a blue rect, but windows never updates your control before it gets painted red.

here you could call This.Invalidate(true) here, but since you are sleeping the thread, a better approach would be calling this.Refresh();

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);
            This.Refresh(); //tells windows to repaint the form
            Thread.Sleep(1000);
            G.FillRectangle(new SolidBrush(Color.Red), R);
            This.Refresh(); //tells windows to repaint the form again,
             //ensuring that your one second wait period is not
            //extended by a busy system.
        }
    }
}
commented: clarifing something about Sleep! +5

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.

Of course you shouldn't invalidate the entire form, you would get the object that is being repainted using the sender property and call invalidate on that.

I was sure this was just pseudo code, or else I might have gone into more details. But in the example danny posted there is a good chance if you just invalidated the rectangles that windows won't get around to updating them before that second is up, so it was yield the same result. or just flash blue then flush red. that's why i just used refresh()

And although its often considered best practice to use invalidate so windows can paint it when its good and ready, often when the effect you want is a blinking or fading effect based on a interval, using refresh on that area will topically yield a better visual result at the cost of some extra system resources. So I find that if i am focusing on an effect that I really want to look good, that I will put it in its own control, and refresh that entire control as necessary.

Also, a timer control would probably work just fine for an alternating color or fading type effect, creating a new thread would work too.

*off topic*
when I first learned flash actionscript I had a lot of trouble with events, as when you called an event from an object for example a movieclip used as a button, in its onRelease handler, the key work "this" would refer to the button movieclip that threw the event, not the object that actually contained the code. - when switching back and fourth between actionscript and C# I sometimes forget that "this" always refers to the object that contains the code. ( although in the case above, I intentionally called refresh on "this" with "this" being the form. for sake of not altering the original code too far.

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.

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.

here is the thing. you are animating the effect from within the paint event, and that is typically not a good idea.

a better approach would be to create a bool value like idk, Bool red = false; local to the class, then in the paint event of the form, if its true paint it red and if its false paint it blue.

then use a timer to change the value after 1000ms and call refresh just after it change the bool and stop the timer. then you have a blue panel until the timer ticks, it will then after be red.

of course when you start the timer is up to you....
really. It would help if you told me your actual intended use. that way I could help you with a better solution.

@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:)

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: look here

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.

commented: Thanks for all the effort:) +5

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?

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.

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.

Leave it to sknake to go all out! Best of luck on your application ddanbe!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.