/************************************************************************************************ * GlassButton - How to create an animating glass button using only GDI+ (and not using WPF). * * * * Original developed by Łukasz Świątkowski - lukasz.swiatkowski@gmail.com * * Form-/Perfomance-/Behavior-Improvements by Fink Christoph - fink.christoph@gmail.com * * * * Feel free to use this control in your application or to improve it in any way! * ***********************************************************************************************/ using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using PushButtonState = System.Windows.Forms.VisualStyles.PushButtonState; namespace EnhancedGlassButton { /// /// Represents a glass button control. /// [ToolboxBitmap(typeof(System.Windows.Forms.Button)), ToolboxItem(true), ToolboxItemFilter("System.Windows.Forms"), Description("Raises an event when the user clicks it.")] public partial class GlassButton : Button { #region " Global Vareables " private System.ComponentModel.IContainer components = null; private Timer timer; #region " Vareables for Drawing " GraphicsPath outerBorderPath; GraphicsPath ContentPath; GraphicsPath GlowClip; GraphicsPath GlowBottomRadial; GraphicsPath ShinePath; GraphicsPath BorderPath; PathGradientBrush GlowRadialPath; LinearGradientBrush ShineBrush; Pen outerBorderPen; Pen BorderPen; Color specialSymbolColor; Brush specialSymbolBrush; Brush ContentBrush; Rectangle rect; Rectangle rect2; #endregion /// /// The ToolTip of the Control. /// readonly ToolTip toolTip = new ToolTip(); /// /// If false, the shine isn't drawn (-> symbolizes an disabled control). /// bool drawShine = true; /// /// Set the trynsperency of the special Symbols. /// readonly int transparencyFactor = 128; #endregion #region " Constructors " /// /// Initializes a new instance of the class. /// public GlassButton() { DoubleBuffered = true; InitializeComponent(); roundCorner = 6; timer.Interval = animationLength / framesCount; base.BackColor = Color.Transparent; BackColor = Color.Black; ForeColor = Color.White; OuterBorderColor = Color.White; InnerBorderColor = Color.Black; ShineColor = Color.White; GlowColor = Color.FromArgb(unchecked((int)(0xFF8DBDFF))); alternativeForm = false; showFocusBorder = false; animateGlow = false; showSpecialSymbol = false; specialSymbol = SpecialSymbols.Play; specialSymbolColor = Color.White; toolTipText = ""; specialSymbolBrush = new SolidBrush(Color.FromArgb(transparencyFactor, specialSymbolColor)); alternativeFocusBorderColor = Color.Black; alternativeFormDirection = Direction.Left; RecalcRect((float)currentFrame / (framesCount - 1f)); SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true); SetStyle(ControlStyles.Opaque, false); SizeChanged += GlassButton_SizeChanged; MouseEnter += GlassButton_MouseEnter; MouseLeave += GlassButton_MouseLeave; GotFocus += GlassButton_GotFocus; LostFocus += GlassButton_LostFocus; } private void InitializeComponent() { components = new System.ComponentModel.Container(); timer = new System.Windows.Forms.Timer(components); SuspendLayout(); // // timer // timer.Tick += timer_Tick; ResumeLayout(false); } /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing) { if (imageButton != null) { imageButton.Parent.Dispose(); imageButton.Parent = null; imageButton.Dispose(); imageButton = null; } DisposeAll(outerBorderPath, ContentPath, GlowClip, GlowBottomRadial, ShinePath, BorderPath, GlowRadialPath, ShineBrush, outerBorderPen, BorderPen, specialSymbolBrush, ContentBrush, toolTip, components); } base.Dispose(disposing); } private void DisposeAll(params IDisposable[] objects) { foreach (var item in objects) { item?.Dispose(); } } #endregion #region " Fields and Properties " private Color backColor; /// /// Gets or sets the background color of the control. /// /// A value representing the background color. [DefaultValue(typeof(Color), "Black")] public new Color BackColor { get => backColor; set { if (!backColor.Equals(value)) { backColor = value; UseVisualStyleBackColor = false; RecalcRect((float)currentFrame / (framesCount - 1f)); OnBackColorChanged(EventArgs.Empty); } } } /// /// Gets or sets the foreground color of the control. /// /// The foreground of the control. [DefaultValue(typeof(Color), "White")] public new Color ForeColor { get => base.ForeColor; set { base.ForeColor = value; RecalcRect((float)currentFrame / (framesCount - 1f)); } } private Color innerBorderColor; /// /// Gets or sets the inner border color of the control. /// /// A value representing the color of the inner border. [DefaultValue(typeof(Color), "Black"), Category("Appearance"), Description("The inner border color of the control.")] public Color InnerBorderColor { get => innerBorderColor; set { if (innerBorderColor != value) { innerBorderColor = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } /// /// Gets or sets the color of the special symbol. /// /// The color of the special symbol. [DefaultValue(typeof(Color), "White"), Category("Appearance"), Description("The inner border color of the control.")] public Color SpecialSymbolColor { get => specialSymbolColor; set { if (specialSymbolColor != value) { specialSymbolColor = value; specialSymbolBrush = new SolidBrush(Color.FromArgb(transparencyFactor, specialSymbolColor)); if (IsHandleCreated) { Invalidate(); } } } } private int roundCorner; /// /// Gets or sets the corner radius. /// /// The corner radius. [DefaultValue(6), Category("Appearance"), Description("The radius of the corners.")] public int CornerRadius { get => roundCorner; set { if (roundCorner != value) { roundCorner = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } string toolTipText; /// /// Gets or sets the tool tip text. /// /// The tool tip text. [DefaultValue(""), Category("Appearance"), Description("The ToolTip-Text of the button. Leave blank to not show a ToolTip.")] public string ToolTipText { get => toolTipText; set { if (toolTipText != value) { toolTipText = value; if (toolTipText.Length > 0) toolTip.SetToolTip(this, toolTipText); if (IsHandleCreated) { Invalidate(); } } } } private bool alternativeForm; /// /// Gets or sets the alternative form. /// /// The alternative form. [DefaultValue(false), Category("Appearance"), Description("Draws the Button in an alternative Form.")] public bool AlternativeForm { get => alternativeForm; set { if (alternativeForm != value) { alternativeForm = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } private bool animateGlow; /// /// Gets or sets a value indicating whether the glow is animated. /// /// true if glow is animated; otherwise, false. [DefaultValue(false), Category("Appearance"), Description("If true the glow is animated.")] public bool AnimateGlow { get => animateGlow; set { if (animateGlow != value) { animateGlow = value; if (IsHandleCreated) { Invalidate(); } } } } private bool showSpecialSymbol; /// /// Gets or sets a value indicating whether a special symbol is drawn. /// /// true if special symbol is drawn; otherwise, false. [DefaultValue(false), Category("Appearance"), Description("If true, the selectet special symbol will be drawn on the button.")] public bool ShowSpecialSymbol { get => showSpecialSymbol; set { if (showSpecialSymbol != value) { showSpecialSymbol = value; if (IsHandleCreated) { Invalidate(); } } } } /// /// List of all aviable special symbols. /// public enum SpecialSymbols { ArrowLeft, ArrowRight, ArrowUp, ArrowDown, Play, Pause, Stop, FastForward, Forward, Backward, FastBackward, Speaker, NoSpeaker, Repeat, RepeatAll, Shuffle } private SpecialSymbols specialSymbol; /// /// Gets or sets the special symbol. /// /// The special symbol. [DefaultValue(typeof(SpecialSymbols), "Play"), Category("Appearance"), Description("Sets the type of the special symbol on the button.")] public SpecialSymbols SpecialSymbol { get => specialSymbol; set { if (specialSymbol != value) { specialSymbol = value; if (IsHandleCreated) { Invalidate(); } } } } public enum Direction { Left, Right } private Direction alternativeFormDirection; /// /// Gets or sets the alternative form direction. /// /// The alternative form direction. [DefaultValue(typeof(Direction), "Left"), Category("Appearance"), Description("Sets the Direction of the alternative Form.")] public Direction AlternativeFormDirection { get => alternativeFormDirection; set { if (alternativeFormDirection != value) { alternativeFormDirection = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } private bool showFocusBorder; /// /// Gets or sets a value indicating whether the focus border is shown. /// /// true if focus border shown; otherwise, false. [DefaultValue(false), Category("Appearance"), Description("Draw the normal Focus-Border. Alternativ Focus-Border will be drawed if false.")] public bool ShowFocusBorder { get => showFocusBorder; set { if (showFocusBorder != value) { showFocusBorder = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } private Color alternativeFocusBorderColor; /// /// Gets or sets the color of the alternative focus border. /// /// The color of the alternative focus border. [DefaultValue(typeof(Color), "Black"), Category("Appearance"), Description("The color of the alternative Focus-Border.")] public Color AlternativeFocusBorderColor { get => alternativeFocusBorderColor; set { if (alternativeFocusBorderColor != value) { alternativeFocusBorderColor = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } private Color outerBorderColor; /// /// Gets or sets the outer border color of the control. /// /// A value representing the color of the outer border. [DefaultValue(typeof(Color), "White"), Category("Appearance"), Description("The outer border color of the control.")] public Color OuterBorderColor { get => outerBorderColor; set { if (outerBorderColor != value) { outerBorderColor = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } private Color shineColor; /// /// Gets or sets the shine color of the control. /// /// A value representing the shine color. [DefaultValue(typeof(Color), "White"), Category("Appearance"), Description("The shine color of the control.")] public Color ShineColor { get => shineColor; set { if (shineColor != value) { shineColor = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } private Color glowColor; /// /// Gets or sets the glow color of the control. /// /// A value representing the glow color. [DefaultValue(typeof(Color), "255,141,189,255"), Category("Appearance"), Description("The glow color of the control.")] public Color GlowColor { get => glowColor; set { if (glowColor != value) { glowColor = value; RecalcRect((float)currentFrame / (framesCount - 1f)); if (IsHandleCreated) { Invalidate(); } } } } private bool isHovered; private bool isFocused; private bool isFocusedByKey; private bool isKeyDown; private bool isMouseDown; private bool isPressed => isKeyDown || (isMouseDown && isHovered); /// /// Gets the state of the button control. /// /// The state of the button control. [Browsable(false)] public PushButtonState State { get { if (!Enabled) { return PushButtonState.Disabled; } if (isPressed) { return PushButtonState.Pressed; } if (isHovered) { return PushButtonState.Hot; } if (isFocused || IsDefault) { return PushButtonState.Default; } return PushButtonState.Normal; } } #endregion #region " Overrided Methods " /// /// Raises the event. /// /// The instance containing the event data. protected override void OnClick(EventArgs e) { isKeyDown = isMouseDown = false; base.OnClick(e); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnEnter(EventArgs e) { isFocused = isFocusedByKey = true; base.OnEnter(e); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnLeave(EventArgs e) { base.OnLeave(e); isFocused = isFocusedByKey = isKeyDown = isMouseDown = false; Invalidate(); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnKeyDown(KeyEventArgs kevent) { if (kevent.KeyCode == Keys.Space) { isKeyDown = true; Invalidate(); } base.OnKeyDown(kevent); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnKeyUp(KeyEventArgs kevent) { if (isKeyDown && kevent.KeyCode == Keys.Space) { isKeyDown = false; Invalidate(); } base.OnKeyUp(kevent); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseDown(MouseEventArgs e) { if (!isMouseDown && e.Button == MouseButtons.Left) { isMouseDown = true; isFocusedByKey = false; Invalidate(); } base.OnMouseDown(e); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseUp(MouseEventArgs e) { if (isMouseDown) { isMouseDown = false; Invalidate(); } base.OnMouseUp(e); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnMouseMove(MouseEventArgs mevent) { base.OnMouseMove(mevent); if (mevent.Button == MouseButtons.None) { return; } if (!ClientRectangle.Contains(mevent.X, mevent.Y)) { if (isHovered) { isHovered = false; Invalidate(); } } else if (!isHovered) { isHovered = true; Invalidate(); } } /// /// Raises the event. /// /// The instance containing the event data. protected override void OnMouseEnter(EventArgs e) { isHovered = true; FadeIn(); Invalidate(); base.OnMouseEnter(e); } /// /// Raises the event. /// /// The instance containing the event data. protected override void OnMouseLeave(EventArgs e) { isHovered = false; FadeOut(); Invalidate(); base.OnMouseLeave(e); } #endregion #region " Painting " /// /// Raises the event. /// /// A that contains the event data. protected override void OnPaint(PaintEventArgs pevent) { var sm = pevent.Graphics.SmoothingMode; pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias; DrawButtonBackground(pevent.Graphics); DrawForegroundFromButton(pevent); DrawButtonForeground(pevent.Graphics); pevent.Graphics.SmoothingMode = sm; } /// /// Draws the button background. /// /// The graphics to draw on. private void DrawButtonBackground(Graphics g) { //white border g.DrawPath(outerBorderPen, outerBorderPath); //content g.FillPath(ContentBrush, ContentPath); //glow if ((isHovered || isAnimating) && !isPressed) { g.SetClip(GlowClip, CombineMode.Intersect); g.FillPath(GlowRadialPath, GlowBottomRadial); g.ResetClip(); } //shine if (drawShine && Enabled) { g.FillPath(ShineBrush, ShinePath); } //black border g.DrawPath(BorderPen, BorderPath); //Draws the special Symbol if (showSpecialSymbol) DrawSpecialSymbol(g); } /// /// Draws the special symbol. /// /// The graphics to draw on. private void DrawSpecialSymbol(Graphics g) { var offset = 15; var LineWidth = Width / 15; var pen = new Pen(specialSymbolBrush, Width / 8) { EndCap = LineCap.ArrowAnchor }; var aPen = new Pen(specialSymbolBrush, Width / 4) { EndCap = LineCap.ArrowAnchor }; var font = new Font("Arial", LineWidth * 4, FontStyle.Bold); switch (specialSymbol) { #region " Arrow Left " case SpecialSymbols.ArrowLeft: g.DrawLine(aPen, Width - Width / 5, Height / 2, Width / 8, Height / 2); break; #endregion #region " Arrow Right " case SpecialSymbols.ArrowRight: g.DrawLine(aPen, Width / 6, Height / 2, Width - Width / 8, Height / 2); break; #endregion #region " Arrow Up " case SpecialSymbols.ArrowUp: g.DrawLine(aPen, Width / 2, Height - Height / 5, Width / 2, Height / 8); break; #endregion #region " Arrow Down " case SpecialSymbols.ArrowDown: g.DrawLine(aPen, Width / 2, Height / 5, Width / 2, Height - Height / 8); break; #endregion #region " Play " case SpecialSymbols.Play: g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 4 + Width / 20, Height / 4), new Point(Width - Width / 4 + Width / 20, Height / 2), new Point(Width / 4 + Width / 20, Height - Height / 4)}); break; #endregion #region " Pause " case SpecialSymbols.Pause: g.FillRectangle(specialSymbolBrush, new Rectangle(Width / 4, Height / 4, (Width / 2 - Width / 10) / 2, Height / 2)); g.FillRectangle(specialSymbolBrush, new Rectangle(Width / 2 + Width / 20, Height / 4, (Width / 2 - Width / 10) / 2, Height / 2)); break; #endregion #region " Stop " case SpecialSymbols.Stop: g.FillRectangle(specialSymbolBrush, new Rectangle(Width / 4 + Width / 20, Height / 4 + Height / 20, Width / 2 - Width / 10, Height / 2 - Width / 10)); break; #endregion #region " FastForward " case SpecialSymbols.FastForward: g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 4, Height / 4), new Point(Width / 2, Height / 2), new Point(Width / 4, Height - Height / 4)}); g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 2, Height / 4), new Point(3 * Width / 4, Height / 2), new Point(Width / 2, Height - Height / 4)}); g.FillRectangle(specialSymbolBrush, new Rectangle(3 * Width / 4, Height / 4, Width / 12, Height / 2)); break; #endregion #region " Forward " case SpecialSymbols.Forward: g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 4 + Width / 12, Height / 4), new Point(Width / 2 + Width / 12, Height / 2), new Point(Width / 4 + Width / 12, Height - Height / 4)}); g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 2 + Width / 12, Height / 4), new Point(3 * Width / 4 + Width / 12, Height / 2), new Point(Width / 2 + Width / 12, Height - Height / 4)}); break; #endregion #region " Backward " case SpecialSymbols.Backward: g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 4 - Width / 12, Height / 2), new Point(Width / 2 - Width / 12, Height / 4), new Point(Width / 2 - Width / 12, Height - Height / 4)}); g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 2 - Width / 12, Height / 2), new Point(3 * Width / 4 - Width / 12, Height / 4), new Point(3 * Width / 4 - Width / 12, Height - Height / 4)}); break; #endregion #region " FastBackward " case SpecialSymbols.FastBackward: g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 4, Height / 2), new Point(Width / 2, Height / 4), new Point(Width / 2, Height - Height / 4)}); g.FillPolygon(specialSymbolBrush, new Point[3]{ new Point(Width / 2, Height / 2), new Point(3 * Width / 4, Height / 4), new Point(3 * Width / 4, Height - Height / 4)}); g.FillRectangle(specialSymbolBrush, new Rectangle(Width / 4 - Width / 12, Height / 4, Width / 12, Height / 2)); break; #endregion #region " Speaker " case SpecialSymbols.Speaker: g.DrawPolygon(new Pen(specialSymbolBrush, Width / 20), new Point[6] { new Point(Width / 2 - Width / 6 - Width / offset, Height / 4 + Height / 10), new Point(Width / 2 - Width / offset, Height / 4 + Height / 10), new Point(Width / 2 + Width / 5 - Width / offset, Height / 4), new Point(Width / 2 + Width / 5 - Width / offset, 3 * Height / 4), new Point(Width / 2 - Width / offset, 3 * Height / 4 - Height / 10), new Point(Width / 2 - Width / 6 - Width / offset, 3 * Height / 4 - Height / 10)}); g.DrawLine(new Pen(specialSymbolBrush, Width / 20), Width / 2 - Width / offset, Height / 4 + Height / 10 + Width / 40, Width / 2 - Width / offset, Height - (Height / 4 + Height / 10 + Width / 40)); break; #endregion #region " NoSpeaker " case SpecialSymbols.NoSpeaker: g.DrawPolygon(new Pen(specialSymbolBrush, Width / 20), new Point[6] { new Point(Width / 2 - Width / 6 - Width / offset, Height / 4 + Height / 10), new Point(Width / 2 - Width / offset, Height / 4 + Height / 10), new Point(Width / 2 + Width / 5 - Width / offset, Height / 4), new Point(Width / 2 + Width / 5 - Width / offset, 3 * Height / 4), new Point(Width / 2 - Width / offset, 3 * Height / 4 - Height / 10), new Point(Width / 2 - Width / 6 - Width / offset, 3 * Height / 4 - Height / 10)}); g.DrawLine(new Pen(specialSymbolBrush, Width / 20), Width / 2 - Width / offset, Height / 4 + Height / 10 + Width / 40, Width / 2 - Width / offset, Height - (Height / 4 + Height / 10 + Width / 40)); g.DrawLine(new Pen(specialSymbolBrush, Width / 20), (int)(Width / 2 - Width / 3.5 - Width / offset), 3 * Height / 4 - Height / 10, Width / 2 + Width / 3 - Width / offset, Height / 4 + Height / 12 + Width / 40); break; #endregion #region " Repeat " case SpecialSymbols.Repeat: g.DrawLine(new Pen(specialSymbolBrush, LineWidth), new Point((int)(Width / 4), (int)(Height / 3)), new Point((int)(Width - Width / 2.4), (int)(Height / 3))); g.DrawArc(new Pen(specialSymbolBrush, LineWidth), (int)(Width - Width * 0.6), (int)(Height / 3), (int)(Width / 3), (int)(Height / 3), 270, 180); g.DrawLine(new Pen(specialSymbolBrush, LineWidth), new Point((int)(Width - Width / 2.4), (int)(Height - Height / 3)), new Point((int)(Width / 3.2), (int)(Height - Height / 3))); g.DrawLine(pen, new Point((int)(Width / 3.2), (int)(Height - Height / 3)), new Point((int)(Width / 4), (int)(Height - Height / 3))); break; #endregion #region " RepeatAll " case SpecialSymbols.RepeatAll: g.DrawLine(new Pen(specialSymbolBrush, LineWidth), new Point((int)(Width / 2.4), (int)(Height / 3)), new Point((int)(Width - Width / 2.4), (int)(Height / 3))); g.DrawArc(new Pen(specialSymbolBrush, LineWidth), (int)(Width - Width * 0.6), (int)(Height / 3), (int)(Width / 3), (int)(Height / 3), 270, 180); g.DrawLine(new Pen(specialSymbolBrush, LineWidth), new Point((int)(Width - Width / 2.4), (int)(Height - Height / 3)), new Point((int)(Width / 2.4), (int)(Height - Height / 3))); g.DrawLine(pen, new Point((int)(Width / 2.4), (int)(Height - Height / 3)), new Point((int)(Width / 3), (int)(Height - Height / 3))); g.DrawArc(new Pen(specialSymbolBrush, LineWidth), (int)(Width / 4), (int)(Height / 3), (int)(Width / 3), (int)(Height / 3), 90, 180); break; #endregion #region " Shuffle " case SpecialSymbols.Shuffle: g.DrawString("1", font, specialSymbolBrush, (Width / 2) / 4, Height / 2 - LineWidth * 2); var sWidth = (int)g.MeasureString("2", font).Width; var sHeigth = (int)g.MeasureString("2", font).Height; g.DrawString("2", font, specialSymbolBrush, Width / 2 - sWidth / 2 - Width / (2 * offset), Height - LineWidth - sHeigth); sWidth = (int)g.MeasureString("3", font).Width; g.DrawString("3", font, specialSymbolBrush, Width - (Width / 2) / 4 - sWidth - Width / (2 * offset), Height / 2 - LineWidth * 2); g.DrawArc(pen, (Width / 2) / 2, Height / 6, Width - (Width / 2), (int)(Height / 2.2), 170, 210); break; #endregion default: break; } } /// /// Draws the button foreground. /// /// The graphics to draw on. private void DrawButtonForeground(Graphics g) { if (ShowFocusBorder && Focused && ShowFocusCues && !alternativeForm) { var rect = ClientRectangle; rect.Inflate(-4, -4); ControlPaint.DrawFocusRectangle(g, rect); } } private Button imageButton; /// /// Draws the foreground from button. /// /// The instance containing the event data. private void DrawForegroundFromButton(PaintEventArgs pevent) { if (imageButton == null) { imageButton = new Button { Parent = new TransparentControl(), BackColor = Color.Transparent }; imageButton.FlatAppearance.BorderSize = 0; imageButton.FlatStyle = FlatStyle.Flat; } if (direction != 0) { imageButton.SuspendLayout(); } imageButton.ForeColor = ForeColor; imageButton.Font = Font; imageButton.RightToLeft = RightToLeft; imageButton.Image = Image; imageButton.ImageAlign = ImageAlign; imageButton.ImageIndex = ImageIndex; imageButton.ImageKey = ImageKey; imageButton.ImageList = ImageList; imageButton.Padding = Padding; imageButton.Size = Size; imageButton.Text = Text; imageButton.TextAlign = TextAlign; imageButton.TextImageRelation = TextImageRelation; imageButton.UseCompatibleTextRendering = UseCompatibleTextRendering; imageButton.UseMnemonic = UseMnemonic; if (direction != 0) { imageButton.ResumeLayout(); } InvokePaint(imageButton, pevent); } sealed class TransparentControl : Control { protected override void OnPaintBackground(PaintEventArgs pevent) { } protected override void OnPaint(PaintEventArgs e) { } } /// /// Creates the round rectangle. /// /// The rectangle. /// The radius. /// private GraphicsPath CreateRoundRectangle(Rectangle rectangle, int radius) { var path = new GraphicsPath(); var l = rectangle.Left; var t = rectangle.Top; var w = rectangle.Width; var h = rectangle.Height; var d = radius << 1; if (alternativeForm) { if (alternativeFormDirection == Direction.Left) { path.AddArc(l, t, h, h, 90, 180); path.AddLine(l + h, t, l + w, t); path.AddCurve(new Point[5] { new Point(l + w, t), new Point(l + w - h / 6, t + h / 4), new Point((int)(l + w - (double)(h / 4.7)), t + h / 2), new Point(l + w - h / 6, t + 3 * h / 4), new Point(l + w, t + h) }); path.AddLine(l + h, t + h, l + w, t + h); } else { path.AddCurve(new Point[5] { new Point(l, t), new Point(l + h / 6, t + h / 4), new Point((int)(l + (double)(h / 4.85)), t + h / 2), new Point(l + h / 6, t + 3 * h / 4), new Point(l, t + h) }); path.AddLine(l, t + h, l + w - h, t + h); path.AddArc(l + w - h, t, h, h, 90, -180); path.AddLine(l + w - h, t, l, t); } } else { path.AddArc(l, t, d, d, 180, 90); // topleft path.AddLine(l + radius, t, l + w - radius, t); // top path.AddArc(l + w - d, t, d, d, 270, 90); // topright path.AddLine(l + w, t + radius, l + w, t + h - radius); // right path.AddArc(l + w - d, t + h - d, d, d, 0, 90); // bottomright path.AddLine(l + w - radius, t + h, l + radius, t + h); // bottom path.AddArc(l, t + h - d, d, d, 90, 90); // bottomleft path.AddLine(l, t + h - radius, l, t + radius); // left } path.CloseFigure(); return path; } /// /// Creates the top round rectangle. /// /// The rectangle. /// The radius. /// private GraphicsPath CreateTopRoundRectangle(Rectangle rectangle, int radius) { var path = new GraphicsPath(); var l = rectangle.Left; var t = rectangle.Top; var w = rectangle.Width; var h = rectangle.Height; var d = radius << 1; if (alternativeForm) { if (alternativeFormDirection == Direction.Left) { path.AddArc(l, t, h * 2, h * 2, 180, 90); path.AddLine(l + h, t, l + w, t); path.AddCurve(new Point[3] { new Point(l + w, t), new Point(l + w - h / 3, t + h / 2), new Point((int)(l + w - (double)(h / 2.35)), t + h)}); } else { path.AddCurve(new Point[3] { new Point(l, t), new Point(l + h / 3, t + h / 2), new Point((int)(l + (double)(h / 2.35)), t + h)}); path.AddLine((int)(l + (double)(h / 2.35)), t + h, l + w - h, t + h); path.AddArc(l + w - h * 2, t, h * 2, h * 2, 0, -90); } } else { path.AddArc(l, t, d, d, 180, 90); // topleft path.AddLine(l + radius, t, l + w - radius, t); // top path.AddArc(l + w - d, t, d, d, 270, 90); // topright path.AddLine(l + w, t + radius, l + w, t + h); // right path.AddLine(l + w, t + h, l, t + h); // bottom path.AddLine(l, t + h, l, t + radius); // left } path.CloseFigure(); return path; } /// /// Creates the bottom radial path. /// /// The rectangle. /// private GraphicsPath CreateBottomRadialPath(Rectangle rectangle) { var path = new GraphicsPath(); RectangleF rect = rectangle; rect.X -= rectangle.Width * .35f; rect.Y -= rectangle.Height * .15f; rect.Width *= 1.7f; rect.Height *= 2.3f; path.AddEllipse(rect); path.CloseFigure(); return path; } /// /// Handles the SizeChanged event of the GlassButton control. /// /// The source of the event. /// The instance containing the event data. private void GlassButton_SizeChanged(object sender, EventArgs e) { RecalcRect((float)currentFrame / (framesCount - 1f)); } /// /// Handles the MouseLeave event of the GlassButton control. /// /// The source of the event. /// The instance containing the event data. private void GlassButton_MouseLeave(object sender, EventArgs e) { RecalcGlow((float)currentFrame / (framesCount - 1f)); } /// /// Handles the MouseEnter event of the GlassButton control. /// /// The source of the event. /// The instance containing the event data. private void GlassButton_MouseEnter(object sender, EventArgs e) { RecalcGlow((float)currentFrame / (framesCount - 1f)); } /// /// Handles the LostFocus event of the GlassButton control. /// /// The source of the event. /// The instance containing the event data. private void GlassButton_LostFocus(object sender, EventArgs e) { RecalcOuterBorder(); } /// /// Handles the GotFocus event of the GlassButton control. /// /// The source of the event. /// The instance containing the event data. private void GlassButton_GotFocus(object sender, EventArgs e) { RecalcOuterBorder(); } /// /// Recalcs the rectangles for drawing. /// /// The glow opacity. private void RecalcRect(float glowOpacity) { try { var rCorner = roundCorner; if (roundCorner > Height / 2) rCorner = Height / 2; if (roundCorner > Width / 2) rCorner = Width / 2; rect = RecalcOuterBorder(); rect = RecalcContent(rect, out rect2); RecalcGlow(glowOpacity); rect2 = RecalcShine(rect2); BorderPath = CreateRoundRectangle(rect, rCorner); BorderPen = new Pen(innerBorderColor); } catch { } } /// /// Recalcs the shine. /// /// The rect2. /// private Rectangle RecalcShine(Rectangle rect2) { var rCorner = roundCorner; if (roundCorner > Height / 2) rCorner = Height / 2; if (roundCorner > Width / 2) rCorner = Width / 2; if (rect2.Width > 0 && rect2.Height > 0) { rect2.Height++; ShinePath = CreateTopRoundRectangle(rect2, rCorner); rect2.Height++; var opacity = 0x99; if (isPressed) opacity = (int)(.4f * opacity + .5f); ShineBrush = new LinearGradientBrush(rect2, Color.FromArgb(opacity, shineColor), Color.FromArgb(opacity / 3, shineColor), LinearGradientMode.Vertical); rect2.Height -= 2; drawShine = true; } else drawShine = false; return rect2; } /// /// Recalcs the glow. /// /// The glow opacity. private void RecalcGlow(float glowOpacity) { var rCorner = roundCorner; if (roundCorner > Height / 2) rCorner = Height / 2; if (roundCorner > Width / 2) rCorner = Width / 2; GlowClip = CreateRoundRectangle(rect, rCorner); GlowBottomRadial = CreateBottomRadialPath(rect); GlowRadialPath = new PathGradientBrush(GlowBottomRadial); var opacity = (int)(0xB2 * glowOpacity + .5f); if (!animateGlow) { if (isHovered) opacity = 255; else opacity = 0; } GlowRadialPath.CenterColor = Color.FromArgb(opacity, glowColor); GlowRadialPath.SurroundColors = new Color[] { Color.FromArgb(0, glowColor) }; } /// /// Recalcs the content. /// /// The rect. /// The rect2. /// private Rectangle RecalcContent(Rectangle rect, out Rectangle rect2) { var rCorner = roundCorner; if (roundCorner > Height / 2) rCorner = Height / 2; if (roundCorner > Width / 2) rCorner = Width / 2; rect.X++; rect.Y++; rect.Width -= 2; rect.Height -= 2; rect2 = rect; rect2.Height >>= 1; ContentPath = CreateRoundRectangle(rect, rCorner); var opacity = isPressed ? 0xcc : 0x7f; ContentBrush = new SolidBrush(Color.FromArgb(opacity, backColor)); return rect; } /// /// Recalcs the outer border. /// /// private Rectangle RecalcOuterBorder() { var rCorner = roundCorner; if (roundCorner > Height / 2) rCorner = Height / 2; if (roundCorner > Width / 2) rCorner = Width / 2; Rectangle rect; rect = ClientRectangle; rect.Width--; rect.Height--; outerBorderPath = CreateRoundRectangle(rect, rCorner); rect.Inflate(1, 1); var region = CreateRoundRectangle(rect, rCorner); Region = new Region(region); rect.Inflate(-1, -1); var col = outerBorderColor; if (Focused && !ShowFocusBorder) col = alternativeFocusBorderColor; outerBorderPen = new Pen(col); return rect; } #endregion #region " Unused Properties & Events " /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new FlatButtonAppearance FlatAppearance => base.FlatAppearance; /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new FlatStyle FlatStyle { get => base.FlatStyle; set => base.FlatStyle = value; } /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new bool UseVisualStyleBackColor { get => base.UseVisualStyleBackColor; set => base.UseVisualStyleBackColor = value; } #endregion #region " Animation Support " private const int animationLength = 300; private const int framesCount = 10; private int currentFrame; private int direction; private bool isAnimating => direction != 0; private void FadeIn() { direction = 1; timer.Enabled = true; } private void FadeOut() { direction = -1; timer.Enabled = true; } private void timer_Tick(object sender, EventArgs e) { if (!timer.Enabled || !animateGlow) { return; } RecalcRect((float)currentFrame / (framesCount - 1f)); Refresh(); currentFrame += direction; if (currentFrame == -1) { currentFrame = 0; timer.Enabled = false; direction = 0; return; } if (currentFrame == framesCount) { currentFrame = framesCount - 1; timer.Enabled = false; direction = 0; } } #endregion } }