A version of a WinForms Rounded GroupBox with transparency

I made something similar a while back and never did anything with it.  I started again today as usual because the interest took me lol.  So there are custom group boxes out there and I have seen a few on Code Project, but in this blog I have added a few different features simply by exposing properties of the Pen and Brush objects.

Examples of my control, these are in the source I have provided below in the link:

FormExample

The object itself

All I have done to maintain functionality and simply extend is to inherit from the GroupBox, like below:

 

namespace WindowsForm_Examples_NET_2
{
    public partial class RoundedGroupBox : GroupBox
    {

 

Next I added some properties to it.  This is the dead easy part, as all I am doing is providing exposure to the properties of the Pen and Brush as well as other things:

        //This is the size of the text of the groupbox, e.g. _groupNameDimensions = e.Graphics.MeasureString(this.Text, this.Font);
        private SizeF _groupNameDimensions;
        //The radius of the corners with a default value of 10
        private float _cornerRadius = 10;
        //The leading text margin of the left side
        private float _leadingTextMargin = 25;
        // I need a graphics path here for the corner arcs and then finally close the figure
        private GraphicsPath _roundBoxPath;
        // The first colour of the gradient
        private Color _gradientFrom = Color.Gray;
        //The second colour of the gradient
        private Color _gradientTo = Color.White;
        //The angle of the gradient
        private float _gradientAngle = 90;
        //The fill type, this could be Fill or Linear Gradient
        private FillType _groupFillType = FillType.Fill;
        //The back colour.  This is the colour within the Graphics path, if you change the controls back color that will be behind the groupbox
        private Color _groupBackColor = Color.White;
        //The colour of the border
        private Color _groupBorderColor = Color.Black;
        //The width of the border
        private int _groupBorderWidth = 1;
        //The dash style of the border, this can be set to Solid
        private DashStyle _groupLineStyle = DashStyle.Solid;
        //A Custom pattern for the dashes like 10,50 etc...
        private float[] _dashPattern = new float[] { 5, 5 };

 

I encapsulate these fields so you will see the properties, I also attach the following attribute for design time purposes:

        [NotifyParentProperty(true)]
        public float[] DashPattern
        {
            get { return _dashPattern; }
            set { _dashPattern = value;
            }
        }

I can also now attach some descriptions and even categories so that it will look tidy inside the Property Box.  I have not added these at this time of writing, but for this one paste into the blog I will.

        [Description("The custom pattern for the Group Box Border")]
        [Category("GroupBox Dash")]
        [NotifyParentProperty(true)]
        public float[] DashPattern
        {
            get { return _dashPattern; }
            set { _dashPattern = value;
            }
        }

This will give you the following behaviour inside the properties window:

 

RoundedCornersProperties

Drawing the curved container

I have not gone into Maths yet as much as I should and want being a programmer so please excuse me if the route I have taken,is in any shape a long and inefficient way.  The approach I took was to identify the key area where I would place an arc.  After this I noted down the sweep angles.  For the purposes of this control they will always sweep a distance of 90 degrees but the starting angle will differ.

 

SweepAngle

With reference to the above image please find the method I wrote in order to draw the container.  I feel I have over complicated the equations which enable the control to adapt to changes in size and also changes to the Font property of the Group Text.

        private PointF p1;
        private PointF p2;
        private PointF p3;
        private PointF p4;
        private PointF p5;
        private PointF p6;
        private PointF p7;
        private PointF p8;


        protected override void OnPaint(PaintEventArgs e)
        {
            //This will give the lines a smoother look.
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            //I assign the size of the GroupBox Text property
            _groupNameDimensions = e.Graphics.MeasureString(this.Text, this.Font);
            //Below I create the coordinates for each point, I rake into account the control margin, the radius, the size of the text and also the leading Text margin.
            p1 = new PointF(_leadingTextMargin > (_cornerRadius * 2) ? _leadingTextMargin : (_cornerRadius * 2), this.Margin.Top);
            p2 = new PointF(p1.X + _cornerRadius + _groupNameDimensions.Width, this.Margin.Top);
            p3 = new PointF(p1.X + (_cornerRadius * 2) + _groupNameDimensions.Width, this.Margin.Top + _cornerRadius + _groupNameDimensions.Height);
            p4 = new PointF(this.Width - _cornerRadius - this.Margin.Right, this.Margin.Top + (_cornerRadius * 2) + _groupNameDimensions.Height);
            p5 = new PointF(this.Width - _cornerRadius - this.Margin.Right, this.Height - _cornerRadius - this.Margin.Bottom);
            p6 = new PointF(this.Margin.Left, this.Height - _cornerRadius - this.Margin.Bottom);
            p7 = new PointF(this.Margin.Left, this.Margin.Top + (_cornerRadius * 2) + _groupNameDimensions.Height);
            p8 = new PointF(_leadingTextMargin > (_cornerRadius * 2) ? _leadingTextMargin - _cornerRadius : _cornerRadius, this.Margin.Top + _cornerRadius + _groupNameDimensions.Height);

            // I instantiate the Graphics Path
            _roundBoxPath = new GraphicsPath();

            //I now add the arcs to the graphics path using a helper function to genrate the required rectangle, bit overkill I will admit
            _roundBoxPath.AddArc(GetRectangle(p1), 180F, 90F);
            _roundBoxPath.AddArc(GetRectangle(p2), 270F, 90F);
            _roundBoxPath.AddArc(GetRectangle(p3), 180F, -90F);
            _roundBoxPath.AddArc(GetRectangle(p4), 270F, 90F);
            _roundBoxPath.AddArc(GetRectangle(p5), 0F, 90F);
            _roundBoxPath.AddArc(GetRectangle(p6), 90F, 90F);
            _roundBoxPath.AddArc(GetRectangle(p7), 180F, 90F);
            _roundBoxPath.AddArc(GetRectangle(p8), 90F, -90F);
            _roundBoxPath.CloseFigure();

            //I define a pen and conitionally assign the prroperties to it
            Pen p = new Pen(GroupBorderColor);
            p.DashStyle = GroupLineStyle;
            p.Width = GroupBorderWidth;
            if (GroupLineStyle == DashStyle.Custom)
            {
                p.DashPattern = DashPattern;
            }
            e.Graphics.DrawPath(p, _roundBoxPath);

            //Here I conditionally assign the way the group box should be filled with colour
            switch (this.GroupFillType)
            {
                case FillType.Fill:
                    e.Graphics.FillPath(new SolidBrush(GroupBackColor), _roundBoxPath);
                    break;
                default:
                    LinearGradientBrush lgb = new LinearGradientBrush(new Rectangle(0,0,this.Width,this.Height), GradientFrom, GradientTo, GradientAngle);
                    e.Graphics.FillPath(lgb, _roundBoxPath);
                    break;
            }
            
            //Here I draw the group name text into the tab like shape.
            e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), new PointF(_leadingTextMargin > (_cornerRadius * 2) ? _leadingTextMargin + _cornerRadius : (_cornerRadius * 2) + _cornerRadius, this.Margin.Top + (_cornerRadius / 2)));

            //To avoid being inconsitent I set the minimum with every time it invalidates so as to keep the tab in propeortion to the groiup box.
            this.MinimumSize = new Size(Convert.ToInt32(_leadingTextMargin + _groupNameDimensions.Width +(_cornerRadius*3)), 0);
        }
        private RectangleF GetRectangle(PointF p)
        {
            return new RectangleF(p, new SizeF(_cornerRadius, _cornerRadius));
        }

Transparency

Even though I have inherited from the groupbox control I can still make this transparent, by setting some of the styles of the form, the crucial one being SupportTransparentBackColor.  To avoid flickering as well when you resize the control I have set the ResizeRedraw flag.

        public RoundedGroupBox()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.SetStyle(ControlStyles.ContainerControl, true);
            this.BackColor = Color.Transparent;
            this.Width = 175;
            this.Height = 75;
        }

 

The Source Code

I have not yet figured out how to upload a file with Windows Live Writer yet so I have placed this file on my server.  Thanks for reading and hope it is of some interest.

 

http://andrewrea.co.uk/RoundedCornerGroupBox/RoundedCornerGroupBox.rar

Published Monday, February 18, 2008 6:02 PM by REA_ANDREW

Comments

# re: A version of a WinForms Rounded GroupBox with transparency

Sunday, August 10, 2008 2:31 PM by Telis

Beautiful!

# re: A version of a WinForms Rounded GroupBox with transparency

Tuesday, January 20, 2009 11:10 AM by José Rui Abreu Mira

Hi, I took the liberty to make some additions to your code and you can get it at www.box.net/.../mdjx50b6u5

BTW, it is a really great component.

Tanks.

# re: A version of a WinForms Rounded GroupBox with transparency

Monday, February 23, 2009 4:57 AM by Kayzer Soze

Greate, Is it free to use in my projects?

# re: A version of a WinForms Rounded GroupBox with transparency

Monday, February 23, 2009 5:06 AM by REA_ANDREW

Yes it is free, Please use how ever you would like! :-)

Leave a Comment

(required) 
(required) 
(optional)
(required)