Ripple.NET: A Windows Forms Demo

Finally, in the OnPaint Event Handler I clear the Form and step through the rippleLIst drawing each of the RippleObjs in it that has a location Point that's not Empty.


  1. I started off by defining a "RippleObj" class which can be found at the bottom of the source code (lines 140-160). Each Ripple we create in the application will be of this type.

  2. Next I create an array of 255 RippleObj objects called rippleList.

  3. Override 3 of the Form's Mouse Event Handlers (OnMouseDown, OnMouseMove and OnMouseUp). When the user clicks the left mouse button down I set the first RippleObj in rippleList to the location of the mouse. Then while the mouse button is still down I increment my counter and set the next RippleObj to the new mouse location. And I continue to do this until the user lets go of the mouse button.

  4. I add a Timer to the Form named timer1. I then assign an Event Handler to the Timer which monitors ticks. At every set interval the Timer1OnClick is called. From there is where I check to see if the mouse is still down and add the Ripple if so.

  5. Finally, in the OnPaint Event Handler I clear the Form and step through the rippleLIst drawing each of the RippleObjs in it that has a location Point that's not Empty. Everytime a Ripple is drawn I increment it's radius so the next time it's drawn larger. The size of the radius also determines the opacity of the Pen that's used to draw the brush. After a Ripple has gone all the way down to completely transparent (0% opaque) it's location is set to Empty so it will no longer draw.

// A simple ripple test.
// I make no warrantees as to it's stability. Use at your own risk.
// If you make any improvements on it please send them to me.
// Written by: James O'Reilly, SynergyMedia, Inc. james@synergymedia.net
//
// To compile at the console:
// csc.exe /out:.\Ripple.exe /t:exe /r:System.dll Ripple.cs

using
System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace SynergyMediaInc
{
class Ripple: Form
{
RippleObj[] rippleList =
new RippleObj[254];
int ripplePntr = 0;
bool mouseDown = false;
Point ptMouse = Point.Empty;
Font font1;
private Timer timer1 = new Timer();
private long ticks = 0;
// if you don't know i'm not telling
public static void Main()
{
Application.Run(
new Ripple());
}
public Ripple()
{
Text = "Ripple";
BackColor = Color.Blue;
ForeColor = Color.White;
ClientSize += ClientSize;
// Double the client area.
font1 = new Font("Times New Roman", 144);
// reduce flicker
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint,
true);
SetStyle(ControlStyles.DoubleBuffer,
true);
// we need a timer to animate
timer1.Interval = 50;
timer1.Tick +=
new System.EventHandler(Timer1OnTick);
timer1.Start();
}
// add a new ripple to the list and flag the mouse as down
protected override void OnMouseDown(MouseEventArgs mea)
{
if (mea.Button == MouseButtons.Left)
{
AddRipple();
mouseDown =
true;
}
}
// record the current mouse location
protected override void OnMouseMove(MouseEventArgs mea)
{
ptMouse =
new Point(mea.X, mea.Y);
}
// flag the mouse as up and stop adding ripples to the list
protected override void OnMouseUp(MouseEventArgs mea)
{
if (mea.Button == MouseButtons.Left)
mouseDown =
false;
}
// add the recorded mouse point to the ripple list
private void AddRipple()
{
rippleList[ripplePntr] =
new RippleObj(ptMouse);
ripplePntr++;
if (ripplePntr >= rippleList.Length)
ripplePntr = 0;
}
// draw the ripples
protected override void OnPaint(PaintEventArgs pea)
{
Color clr;
float w;
Graphics g = pea.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality;
g.Clear(BackColor);
// paint the caption
float cx = g.VisibleClipBounds.Size.Width;
float cy = g.VisibleClipBounds.Size.Height;
SizeF sizef = g.MeasureString(Text, font1);
Brush brush1 =
new SolidBrush(Color.DarkRed);
g.DrawString(Text, font1, brush1,(cx - sizef.Width) / 2,(cy - sizef.Height) / 2);
// loop through the ripple list
for (int i = 0; i < rippleList.Length; i++)
{
if ((rippleList[i] != null) && (rippleList[i].loc != Point.Empty))
{
// set the transparency and line width according to the radius size
clr = Color.FromArgb(255-rippleList[i].rad,255,255,255);
w = 0.03f * rippleList[i].rad;
// draw the ellipse
g.DrawEllipse(new Pen(clr, w),rippleList[i].loc.X - rippleList[i].rad,rippleList[i].loc.Y - rippleList[i].rad/4,rippleList[i].rad * 2,rippleList[i].rad/2);
// expand the radius
rippleList[i].rad += 10;
// see if the ripple is too big
if (rippleList[i].rad > 255)
{
//RemoveRipple(i);
rippleList[i].Reset();
}
}
}
}
// automatically called by the timer according to the interval set
private void Timer1OnTick(object obj, EventArgs ea)
{
ticks++;
// check to see if the user has the button pressed and add a ripple to the list if they do
if (mouseDown)
AddRipple();
// invalidate the graphics object so it will repaint
Invalidate();
}
}
// this class defines the make-up of a ripple object
class RippleObj
{
public Point loc = Point.Empty; // the location of the ripple
public int rad = 0; // the size of it's radius
public RippleObj()
{
Reset();
}
public RippleObj(Point mLoc)
{
Reset();
loc = mLoc;
}
public void Reset()
{
loc = Point.Empty;
rad = 0;
}
}