555 lines
20 KiB
C#
555 lines
20 KiB
C#
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 --
|
|
|
|
/// <summary>
|
|
/// Gets or sets the top button color.
|
|
/// </summary>
|
|
/// <value>The top button color.</value>
|
|
[Browsable(true), DefaultValue(typeof(Color), "CornflowerBlue")]
|
|
[Category("Appearance")]
|
|
public Color ButtonColorTop { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the bottom button color.
|
|
/// </summary>
|
|
/// <value>The bottom button color.</value>
|
|
[Browsable(true), DefaultValue(typeof(Color), "DodgerBlue")]
|
|
[Category("Appearance")]
|
|
public Color ButtonColorBottom { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the color of the pulse.
|
|
/// </summary>
|
|
/// <value>The color of the pulse.</value>
|
|
[Browsable(true), DefaultValue(typeof(Color), "Black")]
|
|
[Category("Appearance")]
|
|
public Color PulseColor { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the type of the shape.
|
|
/// </summary>
|
|
/// <value>The type of the shape.</value>
|
|
[Browsable(true), DefaultValue(typeof(Shape), "Round")]
|
|
[Category("Appearance")]
|
|
public Shape ShapeType { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the corner radius.
|
|
/// </summary>
|
|
/// <value>The corner radius.</value>
|
|
[Browsable(true), DefaultValue(10)]
|
|
[Category("Appearance")]
|
|
public int CornerRadius { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the color of the focus.
|
|
/// </summary>
|
|
/// <value>The color of the focus.</value>
|
|
[Browsable(true), DefaultValue(typeof(Color), "Orange")]
|
|
[Category("Appearance")]
|
|
public Color FocusColor { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the foreground color of the control.
|
|
/// </summary>
|
|
/// <value></value>
|
|
/// <returns>
|
|
/// The foreground <see cref="T:System.Drawing.Color"/> of the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultForeColor"/> property.
|
|
/// </returns>
|
|
/// <PermissionSet>
|
|
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
|
|
/// </PermissionSet>
|
|
[Browsable(true), DefaultValue(typeof(Color), "White")]
|
|
[Category("Appearance")]
|
|
public new Color ForeColor
|
|
{
|
|
get { return base.ForeColor; }
|
|
set { base.ForeColor = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the number of pulses.
|
|
/// </summary>
|
|
/// <value>The number of pulses.</value>
|
|
[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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the width of the pulse.
|
|
/// </summary>
|
|
/// <value>The width of the pulse.</value>
|
|
[Browsable(true), DefaultValue(10)]
|
|
[Category("Appearance")]
|
|
public int PulseWidth
|
|
{
|
|
get { return pulseWidth; }
|
|
set { pulseWidth = value; ArrangePulses(); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the wave speed.
|
|
/// </summary>
|
|
/// <value>The speed of the pulses.</value>
|
|
[Browsable(true), DefaultValue(typeof(float), "0.3f")]
|
|
[Category("Appearance")]
|
|
public float PulseSpeed
|
|
{
|
|
get { return pulseSpeed; }
|
|
set
|
|
{
|
|
if (value <= 0) return;
|
|
pulseSpeed = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the interval.
|
|
/// </summary>
|
|
/// <value>The interval.</value>
|
|
[Browsable(false), DefaultValue(50)]
|
|
public int Interval
|
|
{
|
|
get { return pulseTimer.Interval; }
|
|
set { pulseTimer.Interval = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region -- Constructor --
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PulseButton"/> class.
|
|
/// </summary>
|
|
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 --
|
|
|
|
/// <summary>
|
|
/// Handles the pulse timer tick.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
|
|
private void PulseTimerTick(object sender, EventArgs e)
|
|
{
|
|
pulseTimer.Enabled = false;
|
|
InflatePulses();
|
|
Invalidate();
|
|
pulseTimer.Enabled = true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region -- Protected overrides --
|
|
|
|
#region - Mouse -
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"/> event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseUp(MouseEventArgs e)
|
|
{
|
|
base.OnMouseUp(e);
|
|
if (e.Button != MouseButtons.Left) return;
|
|
pressed = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"/> event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
{
|
|
base.OnMouseDown(e);
|
|
if (e.Button != MouseButtons.Left) return;
|
|
pressed = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="M:System.Windows.Forms.Control.OnMouseMove(System.Windows.Forms.MouseEventArgs)"/> event.
|
|
/// </summary>
|
|
/// <param name="mevent">A <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseMove(MouseEventArgs mevent)
|
|
{
|
|
base.OnMouseMove(mevent);
|
|
mouseOver = centerRect.Contains(mevent.Location);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="JYControl.OnMouseLeave"/> event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseLeave(EventArgs e)
|
|
{
|
|
base.OnMouseLeave(e);
|
|
mouseOver = false;
|
|
pressed = false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="E:System.Windows.Forms.Control.EnabledChanged"/> event.
|
|
/// </summary>
|
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|
protected override void OnEnabledChanged(EventArgs e)
|
|
{
|
|
base.OnEnabledChanged(e);
|
|
pulseTimer.Enabled = Enabled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="E:System.Windows.Forms.Control.Resize"/> event.
|
|
/// </summary>
|
|
/// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
|
|
protected override void OnResize(EventArgs e)
|
|
{
|
|
base.OnResize(e);
|
|
if (pulses == null || pulses.Length == 0) return;
|
|
ArrangePulses();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="E:System.Windows.Forms.Control.Paint"/> event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"/> that contains the event data.</param>
|
|
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 --
|
|
|
|
/// <summary>
|
|
/// Draws the border.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
protected virtual void DrawBorder(Graphics g)
|
|
{
|
|
using (var pen = new Pen(!Focused ? Color.FromArgb(60, Color.Black) : FocusColor, 2))
|
|
PaintShape(g, pen, centerRect);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws the center.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
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);
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws the pulses.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws the text.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws the reflex.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws the high light.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
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));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Paints the shape.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
/// <param name="p">The pen</param>
|
|
/// <param name="rectangle">The rectangle.</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Paints the shape.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
/// <param name="b">The brush</param>
|
|
/// <param name="rectangle">The rectangle.</param>
|
|
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 --
|
|
|
|
/// <summary>
|
|
/// Gets a path of a rectangle with round corners.
|
|
/// </summary>
|
|
/// <param name="g">The graphics object</param>
|
|
/// <param name="rect">The rectangle</param>
|
|
/// <param name="radius">The corner radius</param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the placement.
|
|
/// </summary>
|
|
/// <param name="align">The alignment of the element</param>
|
|
/// <param name="rect">A retangle</param>
|
|
/// <param name="element">The element to be placed</param>
|
|
/// <returns></returns>
|
|
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 --
|
|
|
|
/// <summary>
|
|
/// Arranges the pulses.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inflates the pulses.
|
|
/// </summary>
|
|
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
|
|
|
|
}
|
|
}
|