Click here to Skip to main content
Click here to Skip to main content

SBButton

, 4 Mar 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

[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:

    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:

    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

 
QuestionGood, clean code - but it could be better by using the Decorator pattern... PinmemberRodney Barbati27-Apr-13 18:28 
Rather than have support for all the various drawing portions, like InnerBorder, OuterBorder, etc. built right into the class, you could contain each of the drawing options in their own class. The button (or rather the control base class) would allow you to add any number of these decorators to any given control by maintaining a List of them. During a draw, the control itself would draw, then the control base would loop through all the decorators calling Draw() on each.
 
This will give you the same result as you currently have, but will allow you to use the same decorators on other custom controls you might build.
 
To understand how it might work, have your decorators implement the following interface...
 
interface IAmADecorator
{
public void Initialize(); // init any variables
public void Measure(Control c); // calculate any size variables - would only be called after a resize
public void Draw(PaintEventArgs e);
}
 
The tricky part will be in trying to figure out a way for a control to represent its border in such a way that the OuterBorder class could paint on it - regardless of whether it was a rectangle or a circle or a polygon. Many years ago I used Paths to accomplish this - not sure if that is an option in c sharp, but that is the kind of thing you are looking for.
GeneralVery Nice! PinmemberChristo66710-May-10 8:50 
GeneralButton don´t returning a DialogResult vale PinmemberRVillegas24-Mar-09 21:06 
GeneralRe: Button don´t returning a DialogResult vale PinmemberChristo66718-May-10 23:20 
GeneralGood Control, Needs Demo Pinmemberthomasswilliams6-Mar-08 19:37 
GeneralGood Job! Pinmemberring_02-Mar-08 23:18 
GeneralRe: Good Job! Pinmembermusabshareef2-Mar-08 23:25 
GeneralSource Pinmembermusabshareef2-Mar-08 22:21 
QuestionSource ? PinmemberLautas2-Mar-08 21:43 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150327.1 | Last Updated 5 Mar 2008
Article Copyright 2008 by Musab Shareef
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid