using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Drawing.Drawing2D; using System.Security.Permissions; using System.Windows.Forms; using System.Windows.Forms.Design; using System.Windows.Forms.VisualStyles; using Vanara.Extensions; namespace Vanara.Windows.Forms { /// A custom drawn button. /// public class CustomButton : CustomDrawBase { private const int pad = 3; private RectangleF contentRect; private int cornerRadius = 4; private DrawPattern defDrawPattern; private Corners roundCorners = Corners.All; /// Initializes a new instance of the class. public CustomButton() { SetStyle(ControlStyles.Opaque, false); SetStyle(ControlStyles.DoubleBuffer | ControlStyles.SupportsTransparentBackColor, true); } /// Gets or sets the corner radius. /// The corner radius. [Category("Appearance"), DefaultValue(4), Description("Defines the radius of the controls RoundedCorners.")] public int CornerRadius { get => cornerRadius; set => SetField(ref cornerRadius, value, nameof(CornerRadius)); } /// Gets the paint pattern. /// The paint pattern. public Dictionary PaintPattern { get; } = new Dictionary(); /// Gets or sets the style of the rounded corners. /// The rounded corner style. [Category("Appearance"), DefaultValue(typeof(Corners), "All")] [Description("Gets/sets the corners of the control to round.")] [Editor(typeof(RoundCornersEditor), typeof(UITypeEditor))] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public Corners RoundCorners { get => roundCorners; set => SetField(ref roundCorners, value, nameof(RoundCorners)); } /// Gets the state of the button. /// The state of the button. protected PushButtonState ButtonState { get { EnumFlagIndexer state = State; if (state[ControlState.Disabled]) return PushButtonState.Disabled; if (state[ControlState.Hot]) return PushButtonState.Hot; if (state[ControlState.Pressed]) return PushButtonState.Pressed; if (state[ControlState.Defaulted]) return PushButtonState.Default; return PushButtonState.Normal; } } private DrawPattern DefaultDrawPattern => defDrawPattern ?? (defDrawPattern = new DrawPattern(BackColor, BackColor, ForeColor)); /// Raises the event. /// A that contains the event data. protected override void OnPaint(PaintEventArgs e) { var dp = GetPaintPattern(ButtonState); e.Graphics.DrawImageAndText(Rectangle.Round(contentRect), Text, Font, Image, TextAlign, ImageAlign, TextImageRelation, dp.Text, false, 0, Enabled, this.BuildTextFormatFlags()); DrawFocus(e.Graphics); base.OnPaint(e); } /// Paints the background of the control. /// A that contains information about the control to paint. protected override void OnPaintBackground(PaintEventArgs pevent) { //Simulate Transparency var g = pevent.Graphics.BeginContainer(); var translateRect = Bounds; pevent.Graphics.TranslateTransform(-Left, -Top); var pe = new PaintEventArgs(pevent.Graphics, translateRect); InvokePaintBackground(Parent, pe); InvokePaint(Parent, pe); pevent.Graphics.ResetTransform(); pevent.Graphics.EndContainer(g); pevent.Graphics.SmoothingMode = SmoothingMode.AntiAlias; var dp = GetPaintPattern(ButtonState); var path = new GraphicsPath(); path.AddRoundedRectangle(Squeeze(pevent.ClipRectangle), new Size(CornerRadius, CornerRadius), RoundCorners); //Draw the Button Background pevent.Graphics.FillPath(dp.Fill, path); //...and border pevent.Graphics.DrawPath(dp.Line, path); // Get the Rectangle to be used for Content var xy = Math.Max(Convert.ToSingle(CornerRadius - (CornerRadius / Math.Sqrt(2))), pad); contentRect = RectangleF.Inflate(pevent.ClipRectangle, -xy, -xy); } /// Raises the event. /// An that contains the event data. protected override void OnTextChanged(EventArgs e) { base.OnTextChanged(e); Invalidate(); } private static Rectangle Squeeze(Rectangle r, int val = 1) { var ret = r; ret.Height -= val; ret.Width -= val; return ret; } private void DrawFocus(Graphics g) { if (Focused && ShowFocusCues && TabStop) { var r = Rectangle.Inflate(Rectangle.Round(contentRect), 1, 1); ControlPaint.DrawFocusRectangle(g, r, ForeColor, BackColor); } } private DrawPattern GetPaintPattern(PushButtonState state) { DrawPattern dp; return !PaintPattern.TryGetValue(ButtonState, out dp) ? DefaultDrawPattern : dp; } /// A pattern to use for drawing the button. public class DrawPattern { private Point pt = Point.Empty; /// Initializes a new instance of the class. /// The fill. /// The line. /// The text. /// Index of the image. public DrawPattern(Color fill, Color line, Color text, int imageIndex = -1) : this(line, text, imageIndex) { Fill = fill.IsSystemColor ? SystemBrushes.FromSystemColor(fill) : new SolidBrush(fill); } /// Initializes a new instance of the class. /// The fill1. /// The fill2. /// The pt. /// The line. /// The text. /// Index of the image. public DrawPattern(Color fill1, Color fill2, Point pt, Color line, Color text, int imageIndex = -1) : this(line, text, imageIndex) { SetGradientFill(fill1, fill2, pt); } private DrawPattern(Color line, Color text, int imageIndex) { Line = line.IsSystemColor ? SystemPens.FromSystemColor(line) : new Pen(line, 1); Text = text; ImageIndex = imageIndex; } /// Gets the brush used to fill the pattern. /// The fill brush. public Brush Fill { get; private set; } /// Gets or sets the index of the image. /// The index of the image. public int ImageIndex { get; protected set; } /// Gets the pen used to draw lines. /// The line pen. public Pen Line { get; } /// Gets or sets the linear gradient point. /// The linear gradient point. public Point LinGradPoint { get => pt; set { if (pt != value) { pt = value; var clrs = (Fill as LinearGradientBrush)?.LinearColors; if (clrs != null && clrs.Length == 2) SetGradientFill(clrs[0], clrs[1], pt); } } } /// Gets the text. /// The text. public Color Text { get; } /// Gets the text brush. /// The text brush. public Brush TextBrush => Text.IsSystemColor ? SystemBrushes.FromSystemColor(Text) : new SolidBrush(Text); private void SetGradientFill(Color fill1, Color fill2, Point point) { Fill = new LinearGradientBrush(Point.Empty, point, fill1, fill2) { Blend = new Blend { Positions = new[] { 0, 0.45F, 0.55F, 1 }, Factors = new float[] { 0, 0, 1, 1 } } }; } } } [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] [PermissionSetAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)] internal class RoundCornersEditor : UITypeEditor { public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { if (!(value is Corners) || provider == null) return value; var edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); if (edSvc == null || context == null) return value; var cornerFlags = Corners.None; using (var lb = new CheckedListBox { BorderStyle = BorderStyle.None, CheckOnClick = true }) { lb.Items.Add("TopLeft", (((CustomButton)context.Instance).RoundCorners & Corners.TopLeft) == Corners.TopLeft); lb.Items.Add("TopRight", (((CustomButton)context.Instance).RoundCorners & Corners.TopRight) == Corners.TopRight); lb.Items.Add("BottomLeft", (((CustomButton)context.Instance).RoundCorners & Corners.BottomLeft) == Corners.BottomLeft); lb.Items.Add("BottomRight", (((CustomButton)context.Instance).RoundCorners & Corners.BottomRight) == Corners.BottomRight); edSvc.DropDownControl(lb); foreach (var o in lb.CheckedItems) cornerFlags |= (Corners)Enum.Parse(typeof(Corners), o.ToString()); } edSvc.CloseDropDown(); return cornerFlags; } public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) => UITypeEditorEditStyle.DropDown; } }