Click here to Skip to main content
15,892,746 members
Articles / Desktop Programming / Windows Forms

Shape Control for .NET

Rate me:
Please Sign up or sign in to vote.
4.84/5 (170 votes)
23 Mar 2017CPOL10 min read 380.8K   21.7K   301  
Implementing shape control that supports transparency, custom design-time editors and simple animation
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace ShapeControl
{
	//All the defined shape type
	public enum ShapeType{	Rectangle,
							RoundedRectangle,
		                    Diamond,
							Ellipse, 
							TriangleUp,
							TriangleDown,
							TriangleLeft,
							TriangleRight,
		                    BallonNE,
		                    BallonNW,
							BallonSW,
							BallonSE,
		                    CustomPolygon,
		                    CustomPie,
                            LineDown,
                            LineUp,
                            LineHorizontal,
                            LineVertical
		                    
						}


	public class CustomControl1 : System.Windows.Forms.Control
	{

        private GraphicsPath _custompath = new GraphicsPath();
        private IContainer components;

		ShapeType _shape=ShapeType.Rectangle;
		DashStyle _borderstyle=DashStyle.Solid;
		Color _bordercolor=Color.FromArgb(255,255,0,0);
		int _borderwidth=3;
		GraphicsPath _outline=new GraphicsPath();
		bool _usegradient=false;
        bool _blink = false;
        bool _vibrate = false;
        bool _voffseted = false;
		Color _centercolor=Color.FromArgb(100,255,0,0) ;
		Color _surroundcolor=Color.FromArgb(100,0,255,255);
        private Timer timer1;
        private Timer timer2;
        string _tag2 = "";
        Bitmap _bm;

        [Category("Shape"), Description("Additional user-defined data")]
        public string Tag2
        {
            
            get { return _tag2; }
            set
            {
                _tag2 = value;
            }
        }

        [Category("Shape"), Description("Causes the control to blink")]
        public bool Blink
        {
            get { return _blink; }
            set { 
                
                _blink = value; 
                timer1.Enabled = _blink;
                if (!_blink) this.Visible = true;
            }
        }

        [Category("Shape"), Description("Causes the control to vibrate")]
        public bool Vibrate
        {
            get { return _vibrate; }
            set
            {

                _vibrate = value;
                timer2.Enabled = _vibrate;
                if (!_vibrate)
                    if (_voffseted) { this.Top += 5; _voffseted = false; }
            }
        }

        [Category("Shape"), Description("Background Image to define outline")]
        public Image ShapeImage
        {
            get
            {
                return _bm;
            }
            set
            {
               
  
                if (value != null)
                {
                    _bm = (Bitmap)value.Clone();
                    Width = 150;
                    Height = 150;
                    OnResize(null);
                }
                else
                {
                    if(_bm!=null) 
                       _bm = null;

                    OnResize(null);
                }
            }
        }


		[Category("Shape"),Description("Text to display")]
		public override string Text
		{
			get
			{
                
				return base.Text;
               
			}
			set
			{

				
				  base.Text = value;

			}
		}



		//Overide the BackColor Property to be associated with our custom editor
		[Category("Shape"),Description("Back Color")]
		[BrowsableAttribute(true)]
		[EditorAttribute(typeof(ColorEditor), typeof(System.Drawing.Design.UITypeEditor))]        
		public override Color BackColor
		{
			get
			{
				return base.BackColor;
			}
			set
			{
				base.BackColor = value; this.Refresh(); 

			}
		}


		[Category("Shape"),Description("Using Gradient to fill Shape")]	
		public bool UseGradient
		{
			get{ return _usegradient;}
			set{ _usegradient=value; this.Refresh();}
		}

		//For Gradient Rendering, this is the color at the center of the shape
		[Category("Shape"),Description("Color at center")]
		[BrowsableAttribute(true)]
		[EditorAttribute(typeof(ColorEditor), typeof(System.Drawing.Design.UITypeEditor))]        
		public Color CenterColor
		{
			get {return _centercolor;}
			set { _centercolor=value; this.Refresh();}
		}

        //For Gradient Rendering, this is the color at the edges of the shape
		[Category("Shape"),Description("Color at the edges of the Shape")]
		[BrowsableAttribute(true)]
		[EditorAttribute(typeof(ColorEditor), typeof(System.Drawing.Design.UITypeEditor))]        
		public Color SurroundColor
		{
			get {return _surroundcolor;}
			set { _surroundcolor=value; this.Refresh();}
		}


		[Category("Shape"),Description("Border Width")]
		public int BorderWidth
		{
			get{
                
                return _borderwidth;
            
            }
			set{
				_borderwidth=value;
				if (_borderwidth<0) _borderwidth=0;
                OnResize(null);		
				}
		}

		[Category("Shape"),Description("Border Color")]
		[BrowsableAttribute(true)]
		[EditorAttribute(typeof(ColorEditor), typeof(System.Drawing.Design.UITypeEditor))]        
		public Color BorderColor
		{
			get{return _bordercolor;}
			set{_bordercolor=value;this.Refresh();}
		}

		[Category("Shape"),Description("Border Style")]
		public DashStyle BorderStyle
		{
			get{return _borderstyle;}
			set{_borderstyle=value;this.Refresh();}
		}

		[Category("Shape"),Description("Select Shape")]
		[BrowsableAttribute(true)]
		[EditorAttribute(typeof(ShapeTypeEditor), typeof(System.Drawing.Design.UITypeEditor))]        
		public ShapeType Shape
		{
			get{return _shape;}
			set
			{
				_shape=value;
                if (_shape == ShapeType.LineVertical ||
                    _shape == ShapeType.LineHorizontal ||
                    _shape == ShapeType.LineUp ||
                    _shape == ShapeType.LineDown)
                ForeColor = Color.FromArgb(0, 255, 255, 255);

                if (_shape == ShapeType.LineVertical)
                    this.Width = 20;
                if (_shape == ShapeType.LineHorizontal)
                    this.Height = 20;
			    OnResize(null);		
			}
		}

		public CustomControl1()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
            this.DoubleBuffered = true;
			//Using of Double Buffer allow for smooth rendering 
			//minizing flickering
			this.SetStyle(ControlStyles.SupportsTransparentBackColor |
						  ControlStyles.DoubleBuffer |
						  ControlStyles.AllPaintingInWmPaint |
				          ControlStyles.UserPaint,true);

			//set the default backcolor and font
			this.BackColor=Color.FromArgb(0,255,255,255);
			this.Font =new Font("Arial",12,FontStyle.Bold);
            this.Width = 100;
            this.Height = 100;
            this.Text = "";
		}


		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code

		private void InitializeComponent()
		{
            this.components = new System.ComponentModel.Container();
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.timer2 = new System.Windows.Forms.Timer(this.components);
            this.SuspendLayout();
            // 
            // timer1
            // 
            this.timer1.Interval = 200;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
            // 
            // timer2
            // 
            this.timer2.Interval = 200;
            this.timer2.Tick += new System.EventHandler(this.timer2_Tick);
            // 
            // CustomControl1
            // 
            this.TextChanged += new System.EventHandler(this.ShapeControl_TextChanged);
            this.ResumeLayout(false);

		}

		#endregion

		//This function creates the path for each shape
		//It is also being used by the ShapeTypeEditor to create the various shapes
		//for the Shape property editor UI
		internal static void updateOutline(ref GraphicsPath outline,ShapeType shape,int width,int height,int borderwidth)
		{
            
			switch(shape)
			{

                case ShapeType.LineVertical :
                    outline.AddPolygon(new Point[]{
                        new Point(width /2- borderwidth /2,0),
                        new Point (width /2+ borderwidth /2,0),
                        new Point(width /2+ borderwidth /2,height),
                        new Point(width /2- borderwidth /2,height)
                            }
                      );
                    break;

                case ShapeType.LineHorizontal:
                    outline.AddPolygon(new Point[]{
                        new Point(0, height /2- borderwidth /2),
                        new Point (0,height/2+ borderwidth /2),
                        new Point(width,height/2+ borderwidth /2),
                        new Point(width,height /2- borderwidth /2)
                            }
                    );
                    break;
                case ShapeType.LineUp:

                    outline.AddPolygon(new Point[]{
                        new Point(0,height -borderwidth /2),
                        new Point (borderwidth /2,height),
                        new Point(width,borderwidth/2),
                        new Point(width-borderwidth/2,0)
                                    }
                    );
                    break;

                case ShapeType.LineDown :

                    outline.AddPolygon(new Point[]{
                        new Point(borderwidth /2,0),
                        new Point (0,borderwidth /2),
                        new Point(width-borderwidth/2,height),
                        new Point(width,height-borderwidth /2)
                                    }
                    );
                     
                    break;
                case ShapeType.CustomPie:
					outline.AddPie(0,0,width,height,180,270);
					break;
				case ShapeType.CustomPolygon:
					outline.AddPolygon(new Point[]{
								  new Point(0,0),
						          new Point(width/2,height/4),
						          new Point(width,0),
						          new Point((width*3)/4,height/2),
						          new Point(width,height),
						          new Point(width/2,(height*3)/4),
						          new Point(0,height),
						          new Point(width/4,height/2)
												  }
						);
					break;
				case ShapeType.Diamond :
					outline.AddPolygon(new Point[]{ 
								new Point(0,height/2),
						        new Point(width/2,0),
						        new Point(width,height/2),
						        new Point(width/2,height)
												  });
					break;

				case ShapeType.Rectangle : 
					outline.AddRectangle(new Rectangle(0,0,width,height));
					break;

				case ShapeType.Ellipse: 
					outline.AddEllipse(0,0,width,height);
					break;

				case ShapeType.TriangleUp:
					outline.AddPolygon(new Point[]{new Point(0,height),new Point(width,height),new Point(width/2,0)});
					break;

				case ShapeType.TriangleDown:
					outline.AddPolygon(new Point[]{new Point(0,0),new Point(width,0),new Point(width/2,height)});
					break;

				case ShapeType.TriangleLeft:
					outline.AddPolygon(new Point[]{new Point(width,0),new Point(0,height/2),new Point(width,height)});
					break;

				case ShapeType.TriangleRight:
					outline.AddPolygon(new Point[]{new Point(0,0),new Point(width,height/2),new Point(0,height)});
					break;

				case ShapeType.RoundedRectangle:
					outline.AddArc(0,0,width/4,width/4,180,90);
					outline.AddLine(width/8,0,width-width/8,0);
					outline.AddArc(width-width/4,0,width/4,width/4,270,90);
					outline.AddLine(width,width/8,width,height-width /8);
					outline.AddArc(width-width/4,height-width/4,width/4,width/4,0,90);
					outline.AddLine(width-width/8,height,width/8,height);
					outline.AddArc(0,height-width/4,width/4,width/4,90,90);
					outline.AddLine(0,height-width/8,0,width/8);
					break;
				
				case ShapeType.BallonSW:
					outline.AddArc(0,0,width/4,width/4,180,90);
					outline.AddLine(width/8,0,width-width/8,0);
					outline.AddArc(width-width/4,0,width/4,width/4,270,90);
					outline.AddLine(width,width/8,width,(height*0.75f)-width /8);
					outline.AddArc(width-width/4,(height *0.75f )-width/4,width/4,width/4,0,90);
					outline.AddLine(width-width/8,(height*0.75f),width/8 +(width/4),(height*0.75f));
					outline.AddLine(width/8 +(width/4),height*0.75f,width/8 +(width/8),height);
					outline.AddLine(width/8 +(width/8),height,width/8 +(width/8),(height*0.75f));
					outline.AddLine(width/8 +(width/8),(height*0.75f),width/8,(height*0.75f));
					outline.AddArc(0,(height*0.75f)-width/4,width/4,width/4,90,90);
					outline.AddLine(0,(height*0.75f)-width/8,0,width/8);
					break;

				case ShapeType.BallonSE:
					outline.AddArc(0,0,width/4,width/4,180,90);
					outline.AddLine(width/8,0,width-width/8,0);
					outline.AddArc(width-width/4,0,width/4,width/4,270,90);
					outline.AddLine(width,width/8,width,(height*0.75f)-width /8);
					outline.AddArc(width-width/4,(height *0.75f )-width/4,width/4,width/4,0,90);
					outline.AddLine(width-width/8,(height*0.75f),width-(width/4),(height*0.75f));
					outline.AddLine(width-(width/4),height*0.75f,width-(width/4),height);
					outline.AddLine(width-(width/4),height,width -(3*width/8),(height*0.75f));
					outline.AddLine(width -(3*width/8),(height*0.75f),width/8,(height*0.75f));
					outline.AddArc(0,(height*0.75f)-width/4,width/4,width/4,90,90);
					outline.AddLine(0,(height*0.75f)-width/8,0,width/8);
					break;

				case ShapeType.BallonNW:
					outline.AddArc(width-width/4,(height )-width/4,width/4,width/4,0,90);
					outline.AddLine(width-width/8,(height),width-(width/4),(height));					
					outline.AddArc(0,(height)-width/4,width/4,width/4,90,90);
					outline.AddLine(0,(height)-width/8,0,height *0.25f+width/8);
					outline.AddArc(0,height *0.25f,width/4,width/4,180,90);
					outline.AddLine(width/8,height *0.25f,width/4,height *0.25f);
					outline.AddLine(width/4,height *0.25f,width/4,0);
					outline.AddLine(width/4,0,3*width/8 ,height *0.25f);
					outline.AddLine(3*width/8 ,height *0.25f,width -width/8,height *0.25f);
					outline.AddArc(width-width/4,height *0.25f,width/4,width/4 ,270,90);
					outline.AddLine(width,width/8+height *0.25f,width,(height)-width /8);
					break;

				case ShapeType.BallonNE:
					outline.AddArc(width-width/4,(height )-width/4,width/4,width/4,0,90);
					outline.AddLine(width-width/8,(height),width-(width/4),(height));
					outline.AddArc(0,(height)-width/4,width/4,width/4,90,90);
					outline.AddLine(0,(height)-width/8,0,height *0.25f+width/8);
					outline.AddArc(0,height *0.25f,width/4,width/4,180,90);
					outline.AddLine(width/8,height *0.25f,5*width/8,height *0.25f);
					outline.AddLine(5*width/8,height *0.25f,3*width/4,0);
					outline.AddLine(3*width/4,0,3*width/4 ,height *0.25f);
					outline.AddLine(3*width/4 ,height *0.25f,width -width/8,height *0.25f);
					outline.AddArc(width-width/4,height *0.25f,width/4,width/4 ,270,90);
					outline.AddLine(width,width/8+height *0.25f,width,(height)-width /8);
					break;

				default:break;
			}
		}

		protected override void OnResize(EventArgs e)
		{ 
          if ((this.Width < 0) || (this.Height <= 0)) return;

          if (_bm == null)
            {
               
                _outline = new GraphicsPath();

                updateOutline(ref _outline, _shape, this.Width, this.Height,this.BorderWidth);
            }
            else
            {
                Bitmap bm = (Bitmap)_bm.Clone();
                Bitmap bm2 = new Bitmap(Width, Height);
                System.Diagnostics.Debug.WriteLine(bm2.Width + "," + bm2.Height);
                Graphics.FromImage(bm2).DrawImage(bm, new RectangleF(0, 0, bm2.Width, bm2.Height), new RectangleF(0, 0, bm.Width, bm.Height), GraphicsUnit.Pixel);
                TraceOutline.CTraceOuline trace = new TraceOutline.CTraceOuline();
                string s = trace.TraceOutlineN(bm2, 0, bm2.Height / 2, bm2.Width / 2, Color.Black, Color.White, true, 1);
                Point[] p = trace.StringOutline2Polygon(s);
                _outline = new GraphicsPath();
                _outline.AddPolygon(p);
            }
            if(_outline!=null)
	           this.Region=new Region(_outline);

		   this.Refresh();
		   base.OnResize (e);
		}

		
		
		protected override void OnPaint(PaintEventArgs pe)
		{
			//Rendering with Gradient
			if (_usegradient)
			{
				PathGradientBrush br=new PathGradientBrush(this._outline);
				br.CenterColor=this._centercolor;
				br.SurroundColors=new Color[]{this._surroundcolor};
				pe.Graphics.FillPath(br,this._outline);
			}

			//Rendering with Border
			if(_borderwidth>0)
			{
				Pen p=new Pen(_bordercolor,_borderwidth*2);
				p.DashStyle=_borderstyle;
                if (p.DashStyle == DashStyle.Custom)
                    p.DashPattern = new float[] { 1, 1, 1, 1 };
				pe.Graphics.SmoothingMode=SmoothingMode.HighQuality;
				pe.Graphics.DrawPath(p,this._outline);
				p.Dispose();
			}
				
			//Rendering the text to be at the center of the shape
			StringFormat sf=new StringFormat();
			sf.Alignment=StringAlignment.Center;
			sf.LineAlignment=StringAlignment.Center;
			switch(_shape)
			{
				case ShapeType.BallonNE:
				case ShapeType.BallonNW:
					pe.Graphics.DrawString(this.Text,this.Font,new SolidBrush(this.ForeColor),new RectangleF(0,this.Height*0.25f,this.Width ,this.Height*0.75f),sf);
					break;

				case ShapeType.BallonSE:
				case ShapeType.BallonSW:
				  pe.Graphics.DrawString(this.Text,this.Font,new SolidBrush(this.ForeColor),new RectangleF(0,0,this.Width ,this.Height*0.75f),sf);
			      break;

				default: 
					pe.Graphics.DrawString(this.Text,this.Font,new SolidBrush(this.ForeColor),new Rectangle(0,0,this.Width ,this.Height),sf);
					break;
			}

			// Calling the base class OnPaint

			base.OnPaint(pe);
		}


		private void ShapeControl_TextChanged(object sender, System.EventArgs e)
		{
			this.Refresh();
		}

        private void timer1_Tick(object sender, EventArgs e)
        {
            this.Visible = !this.Visible;
        }

        private void timer2_Tick(object sender, EventArgs e)
        {
            if (!_vibrate) return;
            _voffseted = !_voffseted;
            this.Top =_voffseted ?this.Top -5:this.Top +5;
  

        }
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer (Senior)
Singapore Singapore
Coder. Hacker. Fixer.

Comments and Discussions