using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
namespace JYControl
{
public partial class PulseButton : Button
{
#region -- Members --
private readonly Timer pulseTimer;
private RectangleF[] pulses;
private RectangleF centerRect;
private Color[] pulseColors;
private int pulseWidth;
private bool mouseOver;
private bool pressed;
private float pulseSpeed;
public enum Shape
{
Round,
Rectangle
}
#endregion
#region -- Properties --
///
/// Gets or sets the top button color.
///
/// The top button color.
[Browsable(true), DefaultValue(typeof(Color), "CornflowerBlue")]
[Category("Appearance")]
public Color ButtonColorTop { get; set; }
///
/// Gets or sets the bottom button color.
///
/// The bottom button color.
[Browsable(true), DefaultValue(typeof(Color), "DodgerBlue")]
[Category("Appearance")]
public Color ButtonColorBottom { get; set; }
///
/// Gets or sets the color of the pulse.
///
/// The color of the pulse.
[Browsable(true), DefaultValue(typeof(Color), "Black")]
[Category("Appearance")]
public Color PulseColor { get; set; }
///
/// Gets or sets the type of the shape.
///
/// The type of the shape.
[Browsable(true), DefaultValue(typeof(Shape), "Round")]
[Category("Appearance")]
public Shape ShapeType { get; set; }
///
/// Gets or sets the corner radius.
///
/// The corner radius.
[Browsable(true), DefaultValue(10)]
[Category("Appearance")]
public int CornerRadius { get; set; }
///
/// Gets or sets the color of the focus.
///
/// The color of the focus.
[Browsable(true), DefaultValue(typeof(Color), "Orange")]
[Category("Appearance")]
public Color FocusColor { get; set; }
///
/// Gets or sets the foreground color of the control.
///
///
///
/// The foreground of the control. The default is the value of the property.
///
///
///
///
[Browsable(true), DefaultValue(typeof(Color), "White")]
[Category("Appearance")]
public new Color ForeColor
{
get { return base.ForeColor; }
set { base.ForeColor = value; }
}
///
/// Gets or sets the number of pulses.
///
/// The number of pulses.
[Browsable(true), DefaultValue(3)]
[Category("Appearance")]
public int NumberOfPulses
{
get { return pulses.Length; }
set
{
if (value <= 0) return;
pulses = new RectangleF[value];
pulseColors = new Color[value];
ArrangePulses();
}
}
///
/// Gets or sets the width of the pulse.
///
/// The width of the pulse.
[Browsable(true), DefaultValue(10)]
[Category("Appearance")]
public int PulseWidth
{
get { return pulseWidth; }
set { pulseWidth = value; ArrangePulses(); }
}
///
/// Gets or sets the wave speed.
///
/// The speed of the pulses.
[Browsable(true), DefaultValue(typeof(float), "0.3f")]
[Category("Appearance")]
public float PulseSpeed
{
get { return pulseSpeed; }
set
{
if (value <= 0) return;
pulseSpeed = value;
}
}
///
/// Gets or sets the interval.
///
/// The interval.
[Browsable(false), DefaultValue(50)]
public int Interval
{
get { return pulseTimer.Interval; }
set { pulseTimer.Interval = value; }
}
#endregion
#region -- Constructor --
///
/// Initializes a new instance of the class.
///
public PulseButton()
{
// Control styles
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.UserPaint, true);
InitializeComponent();
// Layout & initialization
SuspendLayout();
pulseWidth = 10;
PulseSpeed = .3f;
ButtonColorTop = Color.CornflowerBlue;
ButtonColorBottom = Color.DodgerBlue;
FocusColor = Color.Orange;
PulseColor = Color.Black;
ShapeType = Shape.Round;
CornerRadius = 10;
Image = null;
base.TextAlign = ContentAlignment.MiddleCenter;
Size = new Size(40, 40);
// Timer
pulseTimer = new Timer { Interval = 50 };
pulseTimer.Tick += PulseTimerTick;
pulses = new RectangleF[3];
pulseColors = new Color[3];
ArrangePulses();
pulseTimer.Enabled = true;
ResumeLayout(true);
}
#endregion
#region -- EventHandlers --
///
/// Handles the pulse timer tick.
///
/// The sender.
/// The instance containing the event data.
private void PulseTimerTick(object sender, EventArgs e)
{
pulseTimer.Enabled = false;
InflatePulses();
Invalidate();
pulseTimer.Enabled = true;
}
#endregion
#region -- Protected overrides --
#region - Mouse -
///
/// Raises the event.
///
/// A that contains the event data.
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button != MouseButtons.Left) return;
pressed = false;
}
///
/// Raises the event.
///
/// A that contains the event data.
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button != MouseButtons.Left) return;
pressed = true;
}
///
/// Raises the event.
///
/// A that contains the event data.
protected override void OnMouseMove(MouseEventArgs mevent)
{
base.OnMouseMove(mevent);
mouseOver = centerRect.Contains(mevent.Location);
}
///
/// Raises the event.
///
/// A that contains the event data.
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
mouseOver = false;
pressed = false;
}
#endregion
///
/// Raises the event.
///
/// An that contains the event data.
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
pulseTimer.Enabled = Enabled;
}
///
/// Raises the event.
///
/// An that contains the event data.
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (pulses == null || pulses.Length == 0) return;
ArrangePulses();
}
///
/// Raises the event.
///
/// A that contains the event data.
protected override void OnPaint(PaintEventArgs e)
{
//base.OnPaint(e);
base.OnPaintBackground(e);
// Set Graphics interpolation and smoothing
Graphics g = e.Graphics;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
// Draw pulses
DrawPulses(g);
if (centerRect.IsEmpty) return;
// Draw center
DrawCenter(g);
// Draw border
DrawBorder(g);
// Image
if (Image != null)
g.DrawImage(Image, centerRect);
// Draw highlight
if (mouseOver)
DrawHighLight(g);
// Reflex
if (!pressed) DrawReflex(g);
// Text
DrawText(g);
}
#endregion
#region -- Protected virtual methods --
///
/// Draws the border.
///
/// The graphics object
protected virtual void DrawBorder(Graphics g)
{
using (var pen = new Pen(!Focused ? Color.FromArgb(60, Color.Black) : FocusColor, 2))
PaintShape(g, pen, centerRect);
}
///
/// Draws the center.
///
/// The graphics object
protected virtual void DrawCenter(Graphics g)
{
if (Enabled)
{
using (var lgb = new LinearGradientBrush(centerRect, ButtonColorTop, ButtonColorBottom,
LinearGradientMode.Vertical))
{
PaintShape(g, lgb, centerRect);
}
}
else
{
using (var lgb = new SolidBrush(Color.Gray))
PaintShape(g, lgb, centerRect);
}
}
///
/// Draws the pulses.
///
/// The graphics object
protected virtual void DrawPulses(Graphics g)
{
if (!Enabled) return;
for (var i = 0; i < pulses.Length; i++)
{
using (var sb = new SolidBrush(pulseColors[i]))
{
PaintShape(g, sb, pulses[i]);
}
}
}
///
/// Draws the text.
///
/// The graphics object
protected virtual void DrawText(Graphics g)
{
var format = new StringFormat(StringFormat.GenericDefault) { Trimming = StringTrimming.EllipsisCharacter };
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
format.FormatFlags ^= StringFormatFlags.LineLimit;
format.HotkeyPrefix = HotkeyPrefix.Show;
SizeF size = g.MeasureString(Text, Font, new SizeF(centerRect.Width, centerRect.Height), format);
RectangleF textRect = GetAlignPlacement(TextAlign, centerRect, size);
using (var sb = new SolidBrush(ForeColor))
g.DrawString(Text, Font, sb, textRect, format);
}
///
/// Draws the reflex.
///
/// The graphics object
protected virtual void DrawReflex(Graphics g)
{
using (var path = new GraphicsPath())
{
RectangleF rect = centerRect;
rect.Height = rect.Height / 2;
if (ShapeType == Shape.Round)
{
path.AddArc(centerRect, -180, 180);
RectangleF reflexRectangle = rect;
reflexRectangle.Offset(0, rect.Height);
reflexRectangle.Height /= 6;
path.AddArc(reflexRectangle, 0, 180);
path.CloseFigure();
}
else
{
rect.Height += rect.Height / 6;
path.AddRectangle(rect);
}
RectangleF area = path.GetBounds();
using (var lgb = new LinearGradientBrush(area, Color.FromArgb(30, Color.White),
Color.FromArgb(60, Color.White), -90))
{
g.FillPath(lgb, path);
}
}
}
///
/// Draws the high light.
///
/// The graphics object
protected virtual void DrawHighLight(Graphics g)
{
RectangleF highlightRect = centerRect;
highlightRect.Inflate(-2, -2);
using (var pen = new Pen(Color.FromArgb(60, Color.White), 4))
{
if (ShapeType == Shape.Round)
g.DrawEllipse(pen, highlightRect);
else
g.DrawPath(pen, GetRoundRect(g, highlightRect, CornerRadius));
}
}
///
/// Paints the shape.
///
/// The graphics object
/// The pen
/// The rectangle.
protected virtual void PaintShape(Graphics g, Pen p, RectangleF rectangle)
{
if (ShapeType == Shape.Round)
g.DrawEllipse(p, rectangle);
else
using (var path = GetRoundRect(g, rectangle, CornerRadius))
g.DrawPath(p, path);
}
///
/// Paints the shape.
///
/// The graphics object
/// The brush
/// The rectangle.
protected virtual void PaintShape(Graphics g, Brush b, RectangleF rectangle)
{
if (ShapeType == Shape.Round)
g.FillEllipse(b, rectangle);
else
using (var path = GetRoundRect(g, rectangle, CornerRadius))
g.FillPath(b, path);
}
#endregion
#region -- Public static methods --
///
/// Gets a path of a rectangle with round corners.
///
/// The graphics object
/// The rectangle
/// The corner radius
///
public static GraphicsPath GetRoundRect(Graphics g, RectangleF rect, float radius)
{
var gp = new GraphicsPath();
var diameter = radius * 2;
gp.AddArc(rect.X + rect.Width - diameter, rect.Y, diameter, diameter, 270, 90);
gp.AddArc(rect.X + rect.Width - diameter, rect.Y + rect.Height - diameter, diameter, diameter, 0, 90);
gp.AddArc(rect.X, rect.Y + rect.Height - diameter, diameter, diameter, 90, 90);
gp.AddArc(rect.X, rect.Y, diameter, diameter, 180, 90);
gp.CloseFigure();
return gp;
}
///
/// Gets the placement.
///
/// The alignment of the element
/// A retangle
/// The element to be placed
///
public static RectangleF GetAlignPlacement(ContentAlignment align, RectangleF rect, SizeF element)
{
// Left & Top (default)
float lft = rect.Left;
float top = rect.Y;
// Right
if ((align & (ContentAlignment.BottomRight | ContentAlignment.MiddleRight | ContentAlignment.TopRight)) != 0)
lft = rect.Right - element.Width;
// Center
else if ((align & (ContentAlignment.BottomCenter | ContentAlignment.MiddleCenter | ContentAlignment.TopCenter)) != 0)
lft = (rect.Width / 2) - (element.Width / 2) + rect.Left;
// Bottom
if ((align & (ContentAlignment.BottomCenter | ContentAlignment.BottomLeft | ContentAlignment.BottomRight)) != 0)
top = rect.Bottom - element.Height;
// Middle
else if ((align & (ContentAlignment.MiddleCenter | ContentAlignment.MiddleLeft | ContentAlignment.MiddleRight)) != 0)
top = (rect.Height / 2) - (element.Height / 2) + rect.Y;
return new RectangleF(lft, top, element.Width, element.Height);
}
#endregion
#region -- Private methods --
///
/// Arranges the pulses.
///
private void ArrangePulses()
{
centerRect = new RectangleF(pulseWidth, pulseWidth, Width - 2 * pulseWidth, Height - 2 * pulseWidth);
for (var i = 1; i <= pulses.Length; i++)
{
pulses[i - 1] = new RectangleF(
pulseWidth * i / (float)pulses.Length,
pulseWidth * i / (float)pulses.Length,
Width - 2 * pulseWidth * i / pulses.Length,
Height - 2 * pulseWidth * i / pulses.Length
);
pulseColors[i - 1] = Color.FromArgb((int)(Math.Min(pulses[i - 1].X * 255 / pulseWidth, 255)), Color.White);
}
}
///
/// Inflates the pulses.
///
private void InflatePulses()
{
for (var i = 0; i < pulses.Length; i++)
{
pulses[i].Inflate(PulseSpeed, PulseSpeed);
if (pulses[i].Width > Width || pulses[i].Height > Height || pulses[i].X < 0 || pulses[i].Y < 0)
pulses[i] = new RectangleF(pulseWidth, pulseWidth, Width - 2 * pulseWidth, Height - 2 * pulseWidth);
pulseColors[i] = Color.FromArgb((int)(Math.Min(pulses[i].X * 255 / pulseWidth, 255)), PulseColor);
}
}
#endregion
}
}