Click here to Skip to main content
15,887,350 members
Articles / Multimedia / GDI+
Article

SimpleLine line and box graphics controls

Rate me:
Please Sign up or sign in to vote.
4.42/5 (22 votes)
9 May 2005CPOL2 min read 81.3K   1.3K   43   18
A line and container box control that support gradients

Sample Image - SimpleLine.jpg

Introduction

Anyone else ever wondered why Microsoft did not include a "simple" line/shape control with .NET? I was getting tired of having to write GDI+ every time I wanted a line on a screen.

Overview

This is the first article I have submitted, so please bear with me on the format. I think I have a lot to offer to the CodeProject community, but I wanted to start with something simple.

First off, I would like to state that this control was developed on my own time. Were this something I was paid to do, I would probably have some better comments in the code, and there would not be any "gotchas" in the controls.

That being said, this project includes three controls:

SimpleLine

The simple line control can function as a line or a box. If it's a line, it can be horizontal or vertical. It can also handle gradients, as a line or a box (see picture above).

The following are some of the properties that can be set through the designer properties box, as well as at run-time:

C#
/// <summary>
/// Enum indicating horizontal or vertical gradient draw
/// </summary>
[Category("Custom")]
public GradientDirection GradientAngle

/// <summary>
/// For lines, this will scale the line
/// to fit right-to-left, or top-to-bottom
/// </summary>
[Category("Custom")]
public bool FitToParent

/// <summary>
/// If set to true, gradient will draw
/// with FillColor and Gradient color
/// </summary>
[Category("Custom")]
public bool UseGradient

/// <summary>
/// Enum indicating horizontal line, vertical line, or Box
/// </summary>
[Category("Custom")]
public LineStyle Style

To draw the actual line (or box) the DrawLine() method is called:

C#
public void DrawLine()
{
  if (this.Parent == null) { return ; }
  //we don't want the control to draw on itself at design time

  if (this.Style == LineStyle.None)
  //default to Horizontal line, when placed on a parent
  {
    _lineStyle = LineStyle.Horizontal ;
    _lineWidth = 1 ;
    this.Left = (Parent.Width /2) - this.Width / 2 ;
    this.Top = Parent.Height / 2 ;
  }
  Graphics g = this.CreateGraphics() ; //create the graphics object
  g.Clear(Parent.BackColor) ;
  Pen pn ;
  if (this.Style == LineStyle.Vertical || this.Style == LineStyle.Horizontal)
    pn = new Pen( LineColor,LineWidth * 2);
  else
    pn = new Pen( LineColor,LineWidth);

  Point pt1 = new Point( 0, 0 );
  Point pt2 ;
  if (this.Style == LineStyle.Horizontal)
  {
    if (FitToParent == true)
    {
      this.Left = 0 ;
      this.Width = Parent.ClientRectangle.Width ;
    }
    this.Height = LineWidth ;
    if (this.Height < 1) { this.Height = 1 ; }
    pt2 = new Point( Width , 0 );
    if (UseGradient == false)
    {
      g.DrawLine( pn, pt1, pt2 );
    }
    else
    {
      Rectangle rect = new Rectangle(new Point(0,0), 
        new Size(this.ClientRectangle.Width,LineWidth)) ;
      if (FillColor == Color.Transparent)
      {FillColor = Parent.BackColor ; }
      {
        LinearGradientBrush lgb = new 
          LinearGradientBrush(rect,FillColor,Gradient,0,false) ;
        g.FillRectangle(lgb,0,0,this.Width,LineWidth) ;
      }
    }
  }
  else if (this.Style == LineStyle.Vertical)
  {
    if (FitToParent == true)
    {
      this.Top = 0 ;
      this.Height = Parent.Height ;
    }
    this.Width = LineWidth ;
    if (this.Width < 1) { this.Width = 1 ; }
    pt2 = new Point( 0, Height ) ;
    if (UseGradient == false)
    {
      g.DrawLine( pn, pt1, pt2 );
    }
    else
    {
      Rectangle rect = new Rectangle(new Point(0,0), 
                       new Size(LineWidth,this.Height)) ;
      if (FillColor == Color.Transparent)
      {FillColor = Parent.BackColor ; }
      {
        LinearGradientBrush lgb = new 
          LinearGradientBrush(rect,FillColor,Gradient,90,false) ;
        g.FillRectangle(lgb,0,0,LineWidth,this.Height) ;
      }
    }
  }
  else if (this.Style == LineStyle.Box )
  {
    if (FitToParent == true)
    {
      this.Top = 0 ;
      this.Left = 0 ;
      this.Width = Parent.Width;
      this.Height = Parent.Height ;
    }
    Rectangle rect = new Rectangle(new Point(0,0), 
                     new Size(this.Width,this.Height)) ;
    if (FillColor == Color.Transparent)
    {FillColor = Parent.BackColor ; }
    if (UseGradient)
    {
      LinearGradientBrush lgb = new LinearGradientBrush(rect, 
       FillColor,Gradient,
       GradientAngle==GradientDirection.Horizontal ? 0 : 90,false) ;
      g.FillRectangle(lgb,0,0,this.Width - LineWidth, 
                           this.Height - LineWidth) ;
    }
    else
    {
      SolidBrush sb = new SolidBrush(FillColor) ;
      g.FillRectangle(sb,0,0,this.Width - LineWidth,this.Height - LineWidth) ;
    }
    decimal mod = Decimal.Remainder((decimal)LineWidth,(decimal)2) ;
    int offset = 0 ;
    if (mod != 0 && LineWidth !=1) { offset = 1 ; }
    rect.Offset(LineWidth/2,LineWidth/2) ;
    rect.Height = rect.Height - LineWidth + offset -1 ;
    rect.Width = rect.Width - LineWidth + offset -1 ;
    if (LineWidth > 0) {g.DrawRectangle(pn,rect) ;}
  }
  g.Dispose() ;
}

SimpleBox

The SimpleBox control "looks" like the SimpleLine control (running in "Box" mode). There is one primary difference: the SimpleBox is a container control. In other words, it behaves similar to a Panel.

To make a user control a container control, all you need to do is add the following attribute to the class:

C#
[Designer("System.Windows.Forms.Design.ParentControlDesigner, 
                              System.Design", typeof(IDesigner))]

TransparentLabel

I have to give credit on this control to Kenneth Hadden. I worked with Ken for about a year at my last job, and he took it upon himself to add this control to the SimpleLine project. Although .NET allows you to specify "transparent" as the background for various controls (such as the .NET Label control), that is not truly transparent. It will accurately display the gradient, if placed on a gradient control, but you would not see another control come through, if it were between the gradient and the Label.

Usage

To use these controls, open up the solution I've included. Compile, and add the three SimpleLine controls to your toolbox, if necessary. Place one of the controls on a form or user control, and set the appropriate properties.

The properties of these controls are fairly straightforward. Make sure to change the "UseGradient" property to true if you want to display a gradient.

Good luck. For what it's worth, I am using these controls in a couple of production apps. The code is not perfect, but I have yet to have a problem.

... and finally

If you like this control, please vote. If you don't like it, please email me at paul@blurious.com, and give me the opportunity to fix or explain before you vote.

Updates:

  • 05-10-05
    • Added code from Eric Woodruff to allow SimpleBox to paint at design time.
    • Added a few code snippets to illustrate how controls function.

License

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


Written By
Choice Genetics US
United States United States
Seasoned IT Professional. Currently the IT Director for Choice Genetics, and also the world-wide manager for IT Projects related to R&D for Groupe Grimaud (our parent company).

I've spent about half my career as a contractor. I've lived all over the place, but currently reside near St. Louis, Missouri. Moved out here from Roseville, California in Feb-2005. No regrets yet.

Over the recent years I've written software for:
- Disposing of radioactive and toxic waste.
- Disposing of surplus inventory during the Decommission of McClellan Air Force Base.
- Facilitating genetic improvement for Swine Breeding.
- Managing children placed in State custody.
- Dealing with commercial trucking delivery schedules.
- Tracking high resolution images from archeological digs.
- Project Management for the roofing industry.
- Processing engines for credit card transactions.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey22-Feb-12 0:21
professionalManoj Kumar Choubey22-Feb-12 0:21 
QuestionAwesome! Thanks! Pin
Ian_76713-Oct-11 12:22
Ian_76713-Oct-11 12:22 
GeneralTransparent Color Pin
morteza5725-Sep-06 0:16
morteza5725-Sep-06 0:16 
GeneralBug in DrawLine() Pin
eezo1-Aug-06 16:15
eezo1-Aug-06 16:15 
GeneralRe: Bug in DrawLine() Pin
Paul Brower2-Aug-06 0:52
Paul Brower2-Aug-06 0:52 
GeneralException occurs Pin
Cannoneer27-Mar-06 0:31
Cannoneer27-Mar-06 0:31 
GeneralRe: Exception occurs Pin
Paul Brower27-Mar-06 0:46
Paul Brower27-Mar-06 0:46 
GeneralRe: Exception occurs Pin
Cannoneer27-Mar-06 1:07
Cannoneer27-Mar-06 1:07 
GeneralGreat control - and an enhancement Pin
markarend3-Nov-05 10:10
markarend3-Nov-05 10:10 
GeneralThanks - and a little enhancement Pin
dsk30378-Jun-05 18:44
dsk30378-Jun-05 18:44 
GeneralRe: Thanks - and a little enhancement Pin
Paul Brower9-Jun-05 5:57
Paul Brower9-Jun-05 5:57 
Great! I got the code. I'll definately integrate it into this project as soon as I have some time.

For anyone that wants to do it themselves, here it is:

using System;<br />
using System.Collections;<br />
using System.ComponentModel;<br />
using System.Drawing;<br />
using System.Data;<br />
using System.Windows.Forms;<br />
using System.Diagnostics;<br />
<br />
namespace simpleline.assemblies<br />
{<br />
	/*<br />
	 * Thanks due to Kenneth Hadden for creating this class, and to <br />
	 * Paul Brower for posting it at codeproject.com. See <br />
	 * http://www.codeproject.com/cs/miscctrl/SimpleLine.asp for <br />
	 * Paul's original article. <br />
	 * <br />
	 * Added: TextTransparency property, Donald Kane June 2005. <br />
	 */<br />
<br />
	/// <summary><br />
	/// Summary description for TransparentLabel.<br />
	/// </summary><br />
	public class TransparentLabel : System.Windows.Forms.Control<br />
	{<br />
		System.Drawing.ContentAlignment _textAlign = ContentAlignment.TopLeft ;<br />
		StringFormat _drawFormat = new StringFormat(  ) ;<br />
		/// <summary><br />
		/// From 0 to 255, specifies transparency of the text itself. <br />
		/// </summary><br />
		int _alphaChannel = 255 ;<br />
		Rectangle _textRect = new Rectangle( 0, 0, 0, 0 ) ;<br />
<br />
		/// <summary> <br />
		/// Required designer variable.<br />
		/// </summary><br />
		private System.ComponentModel.Container components = null;<br />
<br />
		public TransparentLabel()<br />
		{<br />
			// This call is required by the Windows.Forms Form Designer.<br />
			InitializeComponent();<br />
			SetStyle( ControlStyles.Opaque, false ) ;<br />
			SetStyle( ControlStyles.ResizeRedraw, true ) ;<br />
		}<br />
<br />
		protected virtual void InvalidateEx( )<br />
		{<br />
			if ( null == this.Parent ) return ;<br />
			System.Drawing.Rectangle dirtyRect = new Rectangle( this.Location, this.Size ) ;<br />
			this.Parent.Invalidate( dirtyRect, true ) ;<br />
		}<br />
<br />
		private void ResetAlign( )<br />
		{<br />
			switch ( _textAlign )<br />
			{<br />
				case ContentAlignment.BottomLeft:<br />
				case ContentAlignment.MiddleLeft:<br />
				case ContentAlignment.TopLeft:<br />
					_drawFormat.Alignment = StringAlignment.Near ;<br />
					break ;<br />
				case ContentAlignment.BottomCenter:<br />
				case ContentAlignment.MiddleCenter:<br />
				case ContentAlignment.TopCenter:<br />
					_drawFormat.Alignment = StringAlignment.Center ;<br />
					break ;<br />
				case ContentAlignment.BottomRight:<br />
				case ContentAlignment.MiddleRight:<br />
				case ContentAlignment.TopRight:<br />
					_drawFormat.Alignment = StringAlignment.Far ;<br />
					break ;<br />
			}<br />
		}<br />
<br />
		private void ResetRect( )<br />
		{<br />
			Graphics g = this.CreateGraphics( ) ;<br />
			SizeF textSize = g.MeasureString( base.Text, this.Font ) ;<br />
			switch ( _textAlign )<br />
			{<br />
				case ContentAlignment.BottomLeft:<br />
				case ContentAlignment.BottomCenter:<br />
				case ContentAlignment.BottomRight:<br />
					_textRect = new Rectangle( 0, this.ClientRectangle.Height - (int)textSize.Height, this.ClientRectangle.Width, (int)textSize.Height ) ;<br />
					break ;<br />
				case ContentAlignment.MiddleLeft:<br />
				case ContentAlignment.MiddleCenter:<br />
				case ContentAlignment.MiddleRight:<br />
					_textRect = new Rectangle( 0, ((this.ClientRectangle.Height - (int)textSize.Height) / 2), this.ClientRectangle.Width, (int)textSize.Height ) ;<br />
					break ;<br />
				case ContentAlignment.TopLeft:<br />
				case ContentAlignment.TopCenter:<br />
				case ContentAlignment.TopRight:<br />
					_textRect = new Rectangle( 0, 0, this.ClientRectangle.Width, (int)textSize.Height ) ;<br />
					break ;<br />
			}<br />
			g.Dispose( ) ;<br />
		}<br />
<br />
		#region O V E R R I D E S<br />
		protected override CreateParams CreateParams<br />
		{<br />
			get<br />
			{<br />
				CreateParams cp = base.CreateParams ;<br />
				cp.ExStyle |= 0x00000020 ; //WS_EX_TRANSPARENT<br />
				return( cp ) ;<br />
			}<br />
		}<br />
<br />
		protected override void InitLayout()<br />
		{<br />
			base.InitLayout( ) ;<br />
			ResetAlign( ) ;<br />
			ResetRect( ) ;<br />
			// This get's removed immediatly upon the first invocation.<br />
			this.Parent.Paint += new PaintEventHandler(Parent_Paint);<br />
		}<br />
		//Must be stubbed out. If you implement this you will loose your transparent background.<br />
		protected override void OnPaintBackground(PaintEventArgs pevent) <br />
		{ <br />
			//do not allow the background to be painted  <br />
			//Debug.WriteLine( "TransparentLabel(" + this.Name + ")::OnPaintBackground" ) ;<br />
		}<br />
<br />
		protected override void OnResize(EventArgs e)<br />
		{<br />
			base.OnResize (e);<br />
			ResetRect( ) ;<br />
		}<br />
<br />
		protected override void OnPaint(PaintEventArgs e)<br />
		{<br />
			Graphics g = e.Graphics ;<br />
			Color fg = Color.FromArgb( _alphaChannel, ForeColor ) ;<br />
			if ( ! Enabled ) fg = System.Drawing.SystemColors.GrayText ;<br />
			SolidBrush drawBrush = new SolidBrush( fg ) ;<br />
			g.DrawString( base.Text, this.Font, drawBrush, _textRect, _drawFormat ) ;<br />
			drawBrush.Dispose( ) ;<br />
		}<br />
<br />
		protected override void OnEnabledChanged(EventArgs e)<br />
		{<br />
			base.OnEnabledChanged (e);<br />
			this.InvalidateEx( ) ;<br />
			this.Invalidate( ) ;<br />
		}<br />
<br />
		protected override void OnFontChanged(EventArgs e)<br />
		{<br />
			base.OnFontChanged (e);<br />
			ResetRect( ) ;<br />
			this.InvalidateEx( ) ;<br />
			this.Invalidate( ) ;<br />
		}<br />
<br />
		protected override void OnParentChanged(EventArgs e)<br />
		{<br />
			base.OnParentChanged (e);<br />
			this.InvalidateEx( ) ;<br />
			this.Invalidate( ) ;<br />
		}<br />
<br />
		protected override void OnVisibleChanged(EventArgs e)<br />
		{<br />
			base.OnVisibleChanged (e);<br />
			this.InvalidateEx( ) ;<br />
			this.Invalidate( ) ;<br />
		}<br />
<br />
		protected override void OnTextChanged(EventArgs e)<br />
		{<br />
			base.OnTextChanged (e);<br />
			ResetRect( ) ;<br />
			this.InvalidateEx( ) ;<br />
			this.Invalidate( ) ;<br />
		}<br />
<br />
		protected override void OnLocationChanged(EventArgs e)<br />
		{<br />
			base.OnLocationChanged (e);<br />
			this.InvalidateEx( ) ;<br />
			this.Invalidate( ) ;<br />
		}<br />
<br />
<br />
		/// <summary> <br />
		/// Clean up any resources being used.<br />
		/// </summary><br />
		protected override void Dispose( bool disposing )<br />
		{<br />
			if( disposing )<br />
			{<br />
				if(components != null)<br />
				{<br />
					components.Dispose();<br />
				}<br />
				_drawFormat.Dispose( ) ;<br />
			}<br />
			base.Dispose( disposing );<br />
		}<br />
<br />
		#endregion<br />
<br />
		#region Component Designer generated code<br />
		/// <summary> <br />
		/// Required method for Designer support - do not modify <br />
		/// the contents of this method with the code editor.<br />
		/// </summary><br />
		private void InitializeComponent()<br />
		{<br />
			// <br />
			// TransparentLabel<br />
			// <br />
			this.BackColor = System.Drawing.SystemColors.Control;<br />
			this.Name = "TransparentLabel";<br />
			this.Size = new System.Drawing.Size(192, 16);<br />
<br />
		}<br />
		#endregion<br />
<br />
		#region P R O P E R T I E S<br />
		[Browsable( true ),<br />
		DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]<br />
		public override string Text<br />
		{<br />
			get { return base.Text ; }<br />
			set { base.Text = value; this.InvalidateEx( ) ; }<br />
		}<br />
<br />
		[Browsable( true ),<br />
		DesignerSerializationVisibility( DesignerSerializationVisibility.Visible ),<br />
		DefaultValue(ContentAlignment.TopLeft)]<br />
		public System.Drawing.ContentAlignment TextAlign<br />
		{<br />
			get { return( _textAlign ) ; }<br />
			set<br />
			{<br />
				_textAlign = value ;<br />
				this.InvalidateEx( ) ;<br />
				ResetAlign( ) ;<br />
				ResetRect( ) ;<br />
			}<br />
		}<br />
<br />
		[Browsable( true ), <br />
		DesignerSerializationVisibility( DesignerSerializationVisibility.Visible ),<br />
		DefaultValue( 100 ),<br />
		Description( "The percent opacity of the text (0=invisible, 100=fully opaque). " ), <br />
		Category( "Appearance" )]<br />
		public int TextTransparency<br />
		{<br />
			get<br />
			{<br />
				return Convert.ToInt32( _alphaChannel * 100D / 255 ); <br />
			}<br />
			set<br />
			{<br />
				int newAlpha = value > 100 ? 100 : value < 0 ? 0 : value;<br />
				newAlpha = Convert.ToInt32( newAlpha * 255D / 100 ); <br />
				if( newAlpha != _alphaChannel )<br />
				{<br />
					_alphaChannel = newAlpha; <br />
					InvalidateEx(); <br />
				}<br />
			}<br />
		}<br />
<br />
		#endregion<br />
<br />
		#region E V E N T   H A N D L E R S<br />
		private void Parent_Paint(object sender, PaintEventArgs e)<br />
		{<br />
			this.Invalidate( ) ;<br />
			// This get's removed immediatly upon the first invocation, <br />
			// becuase it is just a cludge to stop the text from disapearing when<br />
			// you drop a TranparentLable onto a container.<br />
			// If you don't remove it you get way-to-many paints.<br />
			this.Parent.Paint -= new PaintEventHandler(Parent_Paint);<br />
		}<br />
		#endregion<br />
	}<br />
}<br />
<br />

GeneralFix for simpleBoxContainer Pin
Eric Woodruff9-May-05 9:54
professionalEric Woodruff9-May-05 9:54 
GeneralRe: Fix for simpleBoxContainer Pin
Paul Brower9-May-05 10:18
Paul Brower9-May-05 10:18 
GeneralRe: Fix for simpleBoxContainer Pin
Eric Woodruff9-May-05 14:47
professionalEric Woodruff9-May-05 14:47 
GeneralJust my opinion Pin
Dennis C. Dietrich9-May-05 6:00
Dennis C. Dietrich9-May-05 6:00 
GeneralRe: Just my opinion Pin
Paul Brower9-May-05 7:24
Paul Brower9-May-05 7:24 
GeneralRe: Just my opinion Pin
Dennis C. Dietrich9-May-05 7:59
Dennis C. Dietrich9-May-05 7:59 
GeneralRe: Just my opinion Pin
Paul Brower9-May-05 8:02
Paul Brower9-May-05 8:02 

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.