Click here to Skip to main content
15,393,146 members
Articles / Desktop Programming / Windows Forms
Article
Posted 2 Mar 2008

Stats

51.5K views
1.8K downloads
58 bookmarked

SBButton

Rate me:
Please Sign up or sign in to vote.
3.18/5 (15 votes)
4 Mar 2008CPOL1 min read
A fully customizable .NET 2.0 button control.

SBButtonControl2.PNG

Introduction

SBButton control is a button control from a set of controls (Sysbytes Controls) I'm developing to use with an application I'm writing. This button control can be customized in almost anyway the user prefers. You can have different styles for different states of this control, e.g.: DefaultStyle, MouseOverStyle, MouseDownStyle, and DisabledStyle.

How it works

This control is inherited from the Control class and the IButtonControl interface, and uses the OnPaint event to draw the control using GDI+. This control uses some properties which are different from the Button control provided with .NET. Here are the properties:

  • DefaultStyle
  • MouseOverStyle
  • MouseDownStyle
  • DisabledStyle
  • RoundedCorners
  • ShowFocusCue
  • Image
  • ImageAlignment
  • FocusCueColor

The properties DefaultStyle, MouseOverStyle, MouseDownStyle and DisabledStyle are derived from a class named SBButtonAppearance, and all these properties have some common sub-properties. They are:

  • BackColor1
  • BackColor2
  • HighLightColor
  • GlowColor
  • InnerBorderColor
  • OuterBorderColor
  • TextColor
  • FillMode
  • Font
  • HighLightOpacity1
  • HighLightOpacity2
  • GlowOpacity
  • InnerBorderOpacity

Source code

The source code for the SBButtonAppearanceClass follows:

C#
[Serializable()]
[TypeConverter(typeof(ExpandableObjectConverter))]
public class SBButtonAppearance
{
    public event EventHandler<EventArgs> SBButtonAppearanceChanged;

    private Color _backColor1 = SystemColors.ButtonFace;

    public Color BackColor1
    {
        get { return _backColor1; }
        set 
        {
            _backColor1 = value;
            AppearanceChanged();
        }
    }

    private Color _backColor2 = SystemColors.ButtonFace;

    public Color BackColor2
    {
        get { return _backColor2; }
        set 
        {
            _backColor2 = value;
            AppearanceChanged();
        }
    }

    private Color _outerBorderColor = SystemColors.ControlDarkDark;

    public Color OuterBorderColor
    {
        get { return _outerBorderColor; }
        set 
        {
            _outerBorderColor = value;
            AppearanceChanged();
        }
    }

    private Color _innerBorderColor = SystemColors.ControlLightLight;

    public Color InnerBorderColor
    {
        get { return _innerBorderColor; }
        set 
        {
            _innerBorderColor = value;
            AppearanceChanged();
        }
    }

    private Color _glowColor = SystemColors.ControlLightLight;

    public Color GlowColor
    {
        get { return _glowColor; }
        set 
        { 
            _glowColor = value;
            AppearanceChanged();
        }
    }

    private Color _highLightColor = SystemColors.ControlLightLight;

    public Color HighLightColor
    {
        get { return _highLightColor; }
        set 
        {
            _highLightColor = value;
            AppearanceChanged();
        }
    }

    private Color _textColor = SystemColors.ControlText;

    public Color TextColor
    {
        get { return _textColor; }
        set
        {
            _textColor = value;
            AppearanceChanged();
        }
    }

    private Font _font = SystemFonts.DefaultFont;

    public Font Font
    {
        get { return _font; }
        set
        {
            _font = value;
            AppearanceChanged();
        }
    }

    private LinearGradientMode _fillMode = LinearGradientMode.Horizontal;

    public LinearGradientMode FillMode
    {
        get { return _fillMode; }
        set
        {
            _fillMode = value;
            AppearanceChanged();
        }
    }

    private int _innerBorderOpacity = 200;

    public int InnerBorderOpacity
    {
        get { return _innerBorderOpacity; }
        set
        {
            if (value > 255) value = 255;
            if (value < 0) value = 0;

            _innerBorderOpacity = value;
            AppearanceChanged();
        }
    }

    private int _highLightOpacity1 = 200;

    public int HightLightOpacity1
    {
        get { return _highLightOpacity1; }
        set
        {
            if (value > 255) value = 255;
            if (value < 0) value = 0;

            _highLightOpacity1 = value;
            AppearanceChanged();
        }
    }

    private int _highLightOpacity2 = 150;

    public int HightLightOpacity2
    {
        get { return _highLightOpacity2; }
        set
        {
            if (value > 255) value = 255;
            if (value < 0) value = 0;

            _highLightOpacity2 = value;
            AppearanceChanged();
        }
    }

    private int _glowOpacity = 120;

    public int GlowOpacity
    {
        get { return _glowOpacity; }
        set
        {
            if (value > 255) value = 255;
            if (value < 0) value = 0;

            _glowOpacity = value;
            AppearanceChanged();
        }
    }

    public override string ToString()
    {
        return null;
    }

    private void AppearanceChanged()
    {
        EventHandler<EventArgs> temp = SBButtonAppearanceChanged;
        if (temp != null)
            temp(this, new EventArgs());
    }
}

The code for drawing the button:

C#
protected override void OnPaint(PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
    e.Graphics.TextRenderingHint =
      System.Drawing.Text.TextRenderingHint.SystemDefault;

    if(!isMouseIn && !isMouseDown && this.Enabled)
        this.DrawDefault(e);

    if (isMouseIn && !isMouseDown && this.Enabled)
        this.DrawMouseOver(e);

    if (isMouseIn && isMouseDown && this.Enabled)
        this.DrawMouseDown(e);

    if (!isMouseIn && isMouseDown && this.Enabled)
        this.DrawMouseDown(e);

    if (!this.Enabled)
        this.DrawDisabled(e);

    if (this.Focused && this.ShowFocusCue && this.Enabled)
        ControlPaint.DrawBorder(e.Graphics, new Rectangle(
            2, 2, this.Width - 4, this.Height - 4), this.FocusCueColor,
            ButtonBorderStyle.Dashed);
}
private void DrawDefault(PaintEventArgs e)
{
    LinearGradientBrush brBackground = new LinearGradientBrush(
        this.ClientRectangle, this.DefaultStyle.BackColor1,
        this.DefaultStyle.BackColor2, this.DefaultStyle.FillMode);
    LinearGradientBrush brHighlight = new LinearGradientBrush(new Rectangle(
        2, 2, this.Width - 5, this.Height / 2),
        Color.FromArgb(this.DefaultStyle.HightLightOpacity1,
        this.DefaultStyle.HighLightColor),
        Color.FromArgb(this.DefaultStyle.HightLightOpacity2,
        this.DefaultStyle.HighLightColor), LinearGradientMode.Vertical);
    LinearGradientBrush brGlow = new LinearGradientBrush(new Rectangle(
        0, this.Height - this.Height / 4 - 1, this.Width - 1, this.Height / 4),
        Color.Transparent, Color.FromArgb(this.DefaultStyle.GlowOpacity,
        this.DefaultStyle.GlowColor), LinearGradientMode.Vertical);

    Pen pnOuterBorder = new Pen(this.DefaultStyle.OuterBorderColor, 1);
    Pen pnInnerBorder = new Pen(Color.FromArgb(DefaultStyle.InnerBorderOpacity,
        this.DefaultStyle.InnerBorderColor));

    GraphicsPath gpBackground = Common.RoundedRect(new Rectangle(
        0, 0, this.Width - 1, this.Height - 1), 3);
    GraphicsPath gpGlow = Common.RoundedRect(new Rectangle(
        1, this.Height - this.Height / 4, this.Width - 3, this.Height / 4), 1,1,3,3);
    GraphicsPath gpHighlight = Common.RoundedRect(new Rectangle(
        2, 2, this.Width - 5, this.Height / 2 - 1), 3, 3, 1, 1);
    GraphicsPath gpOuterBorder = Common.RoundedRect(new Rectangle(
        0, 0, this.Width - 1, this.Height - 1), 3);
    GraphicsPath gpInnerBorder = Common.RoundedRect(new Rectangle(
        1, 1, this.Width - 3, this.Height - 3), 3);

    Rectangle rectBackground = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
    Rectangle rectGlow = new Rectangle(1, this.Height - this.Height / 4,
        this.Width - 3, this.Height / 4);
    Rectangle rectHighlight = new Rectangle(2, 2, this.Width - 5, this.Height / 2 - 1);
    Rectangle rectOuterBorder = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
    Rectangle rectInnerBorder = new Rectangle(1, 1, this.Width - 3, this.Height - 3);

    Size textSize = TextRenderer.MeasureText(this.Text, this.DefaultStyle.Font);

    Point textPos = new Point(this.Width / 2 - textSize.Width / 2,
        this.Height / 2 - textSize.Height / 2);
    Point imagePos = new Point();

    switch (this.ImageAlignment)
    {
        case Alignment.Right:
            if (this.Image != null)
            {
                textPos = new Point(5, this.Height / 2 - textSize.Height / 2);
                imagePos = new Point(this.Width - this.Image.Width - 5,
                    this.Height / 2 - this.Image.Size.Height / 2);
            }
            break;
        case Alignment.Left:
            if (this.Image != null)
            {
                textPos = new Point(this.Width - textSize.Width - 5,
                    this.Height / 2 - textSize.Height / 2);
                imagePos = new Point(5, this.Height / 2 - this.Image.Size.Height / 2);
            }
            break;
    }

    if (this.RoundedCorners)
    {
        e.Graphics.FillPath(brBackground, gpBackground);
        e.Graphics.FillPath(brGlow, gpGlow);
        e.Graphics.FillPath(brHighlight, gpHighlight);
        e.Graphics.DrawPath(pnOuterBorder, gpOuterBorder);
        e.Graphics.DrawPath(pnInnerBorder, gpInnerBorder);
    }
    else
    {
        e.Graphics.FillRectangle(brBackground, rectBackground);
        e.Graphics.FillRectangle(brGlow, rectGlow);
        e.Graphics.FillRectangle(brHighlight, rectHighlight);
        e.Graphics.DrawRectangle(pnOuterBorder, rectOuterBorder);
        e.Graphics.DrawRectangle(pnInnerBorder, rectInnerBorder);
    }

    if (this.Image != null)
        e.Graphics.DrawImage(this.Image, imagePos.X, imagePos.Y,
            this.Image.Width, this.Image.Height);

    TextRenderer.DrawText(e.Graphics, this.Text, this.DefaultStyle.Font,
        textPos, this.DefaultStyle.TextColor);
}

The methods DrawMouseOver, DrawMouseDown, and DrawDisabled are the same as the DrawDefault except for the colors and opacity values they use. You may have noticed that this method calls a function Common.RoundedRect. This function will return a rectangle with rounded corners as GraphicsPath.

Here is the code for this function:

C#
public static System.Drawing.Drawing2D.GraphicsPath RoundedRect(
    System.Drawing.Rectangle baseRect, int topLeftRadius,
    int topRightRadius, int bottomLeftRadius, int bottomRightRadius)
{
    int topLeftDiameter = topLeftRadius * 2;
    int topRightDiameter = topRightRadius * 2;
    int bottomLeftDiameter = bottomLeftRadius * 2;
    int bottomRightDiameter = bottomRightRadius * 2;

    System.Drawing.Drawing2D.GraphicsPath gp =
           new System.Drawing.Drawing2D.GraphicsPath();

    System.Drawing.Rectangle rectTopLeft = new System.Drawing.Rectangle(
        baseRect.Left, baseRect.Top, topLeftDiameter, topLeftDiameter);
    System.Drawing.Rectangle rectTopRight = new System.Drawing.Rectangle(
        baseRect.Right - topRightDiameter, baseRect.Top, topRightDiameter,
        topRightDiameter);
    System.Drawing.Rectangle rectBottomLeft = new System.Drawing.Rectangle(
        baseRect.Left, baseRect.Bottom - bottomLeftDiameter, bottomLeftDiameter,
        bottomLeftDiameter);
    System.Drawing.Rectangle rectBottomRight = new System.Drawing.Rectangle(
        baseRect.Right - bottomRightDiameter, baseRect.Bottom - bottomRightDiameter,
        bottomRightDiameter, bottomRightDiameter);

    gp.AddArc(rectTopLeft, 180, 90);
    gp.AddArc(rectTopRight, 270, 90);
    gp.AddArc(rectBottomRight, 0, 90);
    gp.AddArc(rectBottomLeft, 90, 90);

    gp.CloseFigure();

    return gp;
}
public static System.Drawing.Drawing2D.GraphicsPath RoundedRect(
    System.Drawing.Rectangle baseRect, int cornerRadius)
{
    int diameter = cornerRadius * 2;

    System.Drawing.Drawing2D.GraphicsPath gp =
           new System.Drawing.Drawing2D.GraphicsPath();

    System.Drawing.Rectangle rectTopLeft = new System.Drawing.Rectangle(
        baseRect.Left, baseRect.Top, diameter, diameter);
    System.Drawing.Rectangle rectTopRight = new System.Drawing.Rectangle(
        baseRect.Right - diameter, baseRect.Top, diameter, diameter);
    System.Drawing.Rectangle rectBottomLeft = new System.Drawing.Rectangle(
        baseRect.Left, baseRect.Bottom - diameter, diameter, diameter);
    System.Drawing.Rectangle rectBottomRight = new System.Drawing.Rectangle(
        baseRect.Right - diameter, baseRect.Bottom - diameter, diameter, diameter);

    gp.AddArc(rectTopLeft, 180, 90);
    gp.AddArc(rectTopRight, 270, 90);
    gp.AddArc(rectBottomRight, 0, 90);
    gp.AddArc(rectBottomLeft, 90, 90);

    gp.CloseFigure();

    return gp;
}

Points of interest

This control supports an image, and it can only be aligned to either right or left. If there is no image, the text will always be aligned to the center of the control. You can modify these behaviours very easily by editing the methods DrawDefault, DrawMouseOver, DrawMouseDown, and DrawDisabled.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Musab Shareef
Software Developer
Maldives Maldives
No Biography provided

Comments and Discussions

 
QuestionAdded RoundedCornerRadius and the Glow gets messed up Pin
Tim8w4-Feb-21 18:27
MemberTim8w4-Feb-21 18:27 
QuestionGood, clean code - but it could be better by using the Decorator pattern... Pin
Rodney Barbati27-Apr-13 17:28
MemberRodney Barbati27-Apr-13 17:28 
GeneralVery Nice! Pin
Christo66710-May-10 7:50
MemberChristo66710-May-10 7:50 
GeneralButton don´t returning a DialogResult vale Pin
RVillegas24-Mar-09 20:06
MemberRVillegas24-Mar-09 20:06 
GeneralRe: Button don´t returning a DialogResult vale Pin
Christo66718-May-10 22:20
MemberChristo66718-May-10 22:20 
GeneralGood Control, Needs Demo Pin
thomasswilliams6-Mar-08 18:37
Memberthomasswilliams6-Mar-08 18:37 
GeneralGood Job! Pin
ring_02-Mar-08 22:18
Memberring_02-Mar-08 22:18 
Though there are loads of same work around .But this one is great.Clear and simple code.
Keep it up

Man having 1 bit brain with parity error

GeneralRe: Good Job! Pin
Musab Shareef2-Mar-08 22:25
MemberMusab Shareef2-Mar-08 22:25 
GeneralSource Pin
Musab Shareef2-Mar-08 21:21
MemberMusab Shareef2-Mar-08 21:21 
QuestionSource ? Pin
mag132-Mar-08 20:43
Membermag132-Mar-08 20:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.