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

.NET Shape Control

Rate me:
Please Sign up or sign in to vote.
1.91/5 (9 votes)
3 Aug 2005 64.8K   2.6K   24   17
Drawing shapes in GDI+.

Sample Image

Introduction

The Shape control was designed to re-create the control that was lost when upgrading to the .NET Framework. This Shape control has much better control over the look.

Using the code

Pretty easy, just plop it on your screen and start playing with the properties.

Points of Interest

I originally wrote this control to get acquainted with drawing in the .NET Framework and the C# language. I had a lot of fun doing it, and learned a lot about the basics of GDI+. I think that it would make a really good tutorial for someone trying to learn the basic flow of a custom graphical control. I would appreciate if anyone adds some functionality, that you would email me so that I can add it in to my source. Enjoy!!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I am currently a software developer for Dearman Systems, Inc..

Comments and Discussions

 
QuestionBusiness use Pin
solitonwave22-Oct-08 18:15
solitonwave22-Oct-08 18:15 
QuestionNot really an article, is it? Pin
T1TAN17-Jul-07 4:36
T1TAN17-Jul-07 4:36 
Generalresize and relocate by mouse Pin
Jian123456710-Jan-06 4:38
Jian123456710-Jan-06 4:38 
GeneralRe: resize and relocate by mouse Pin
Sautin.net10-Jan-06 5:43
Sautin.net10-Jan-06 5:43 
GeneralRe: resize and relocate by mouse Pin
Jian123456710-Jan-06 6:54
Jian123456710-Jan-06 6:54 
GeneralRe: resize and relocate by mouse Pin
Sautin.net10-Jan-06 8:23
Sautin.net10-Jan-06 8:23 
GeneralRe: resize and relocate by mouse Pin
jatm14-Jun-06 18:33
jatm14-Jun-06 18:33 
GeneralRe: resize and relocate by mouse Pin
ipsharer17-Jun-08 7:44
ipsharer17-Jun-08 7:44 
Here is some code from my own implementation of a shape. It should not be too hard to adapt this for this control.
//////////////////////////////////////////////////////////////////////////////////////
//
// Some vectors which we need if we size the shape too small and the cursor exceeds the area of the shape
// then we should know when to start resizing again
//
protected Vector2d m_MouseOverflowRightDown;
protected Vector2d m_MouseOverflowLeftTop;
protected Vector2d m_MouseOverflowRightTop;
protected Vector2d m_MouseOverflowLeftBottom;
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
// Grab points which we need to indicate that the user can resize the object
//
protected RectangleF m_LeftGrabPoint;
protected RectangleF m_TopGrabPoint;
protected RectangleF m_RightGrabPoint;
protected RectangleF m_BottomGrabPoint;

protected RectangleF m_LeftTopGrabPoint;
protected RectangleF m_RightTopGrabPoint;
protected RectangleF m_RightBottomGrabPoint;
protected RectangleF m_LeftBottomGrabPoint;
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
// A variable which helps us to keep the right resize direction
//
protected ResizeDirection m_ResizeDirection = ResizeDirection.None;
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
// This should be located somewhere in the mouse down...
//
#region Start resizing
if (m_LeftGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeWE;
   m_ResizeDirection = ResizeDirection.Left;
}
else if (m_LeftTopGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeNWSE;
   m_ResizeDirection = ResizeDirection.LeftTop;
}
else if (m_TopGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeNS;
   m_ResizeDirection = ResizeDirection.Top;
}
else if (m_RightTopGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeNESW;
   m_ResizeDirection = ResizeDirection.RightTop;
}
else if (m_RightGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeWE;
   m_ResizeDirection = ResizeDirection.Right;
}
else if (m_RightBottomGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeNWSE;
   m_ResizeDirection = ResizeDirection.RightBottom;
}
else if (m_BottomGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeNS;
   m_ResizeDirection = ResizeDirection.Bottom;
}
else if (m_LeftBottomGrabPoint.Contains(e.Location))
{
   Cursor.Current = Cursors.SizeNESW;
   m_ResizeDirection = ResizeDirection.LeftBottom;
}
else
{
   Cursor.Current = Cursors.Default;
   m_ResizeDirection = ResizeDirection.None;
}
#endregion


//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
//...and this shoul be located somewhere in the mouse move
//
if (m_MouseDown &&
    e.Button == MouseButtons.Left &&
    m_ResizeDirection != ResizeDirection.None)
{
   Resize(m_ResizeDirection, delta);
}
// If the mouse only hovers over the shape and hits a grab point
// then we indicate a possible resize by changing the current cursor
else if (Selected && m_ResizeDirection == ResizeDirection.None)
{
   #region Indicate a possible resize

   if (m_LeftGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeWE;
   }
   else if (m_LeftTopGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeNWSE;
   }
   else if (m_TopGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeNS;
   }
   else if (m_RightTopGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeNESW;
   }
   else if (m_RightGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeWE;
   }
   else if (m_RightBottomGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeNWSE;
   }
   else if (m_BottomGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeNS;
   }
   else if (m_LeftBottomGrabPoint.Contains(e.Location))
   {
      Cursor.Current = Cursors.SizeNESW;
   }
   else
   {
      Cursor.Current = Cursors.Default;
   }

   #endregion
}
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
// In the mouse up there should then be something like this
//
m_ResizeDirection = ResizeDirection.None;
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
// The Resize() method which is used above
//
protected virtual void Resize(ResizeDirection direction, Vector2d delta)
{
   RectangleF currentBounds = BorderBounds;
   RectangleF newBounds = new RectangleF();

   #region Resize

   switch (direction)
   {
      case ResizeDirection.Left:
         newBounds.X = currentBounds.X + (int)delta.X;
         newBounds.Y = currentBounds.Y;
         newBounds.Width = currentBounds.Width + (int)delta.X * (-1);
         newBounds.Height = currentBounds.Height;
         break;
      case ResizeDirection.Top:
         newBounds.X = currentBounds.X;
         newBounds.Y = currentBounds.Y + (int)delta.Y;
         newBounds.Width = currentBounds.Width;
         newBounds.Height = currentBounds.Height + (int)delta.Y * (-1);
         break;
      case ResizeDirection.Right:
         newBounds.X = currentBounds.X;
         newBounds.Y = currentBounds.Y;
         newBounds.Width = currentBounds.Width + (int)delta.X;
         newBounds.Height = currentBounds.Height;
         break;
      case ResizeDirection.Bottom:
         newBounds.X = currentBounds.X;
         newBounds.Y = currentBounds.Y;
         newBounds.Width = currentBounds.Width;
         newBounds.Height = currentBounds.Height + (int)delta.Y;
         break;
      case ResizeDirection.LeftTop:
         newBounds.X = currentBounds.X + (int)delta.X;
         newBounds.Y = currentBounds.Y;
         newBounds.Width = currentBounds.Width + (int)delta.X * (-1);
         newBounds.Height = currentBounds.Height;
         newBounds.X = newBounds.X;
         newBounds.Y = newBounds.Y + (int)delta.Y;
         newBounds.Width = newBounds.Width;
         newBounds.Height = newBounds.Height + (int)delta.Y * (-1);
         break;
      case ResizeDirection.RightTop:
         newBounds.X = currentBounds.X;
         newBounds.Y = currentBounds.Y;
         newBounds.Width = currentBounds.Width + (int)delta.X;
         newBounds.Height = currentBounds.Height;
         newBounds.X = newBounds.X;
         newBounds.Y = newBounds.Y + (int)delta.Y;
         newBounds.Width = newBounds.Width;
         newBounds.Height = newBounds.Height + (int)delta.Y * (-1);
         break;
      case ResizeDirection.RightBottom:
         newBounds.X = currentBounds.X;
         newBounds.Y = currentBounds.Y;
         newBounds.Width = currentBounds.Width + (int)delta.X;
         newBounds.Height = currentBounds.Height;
         newBounds.X = newBounds.X;
         newBounds.Y = newBounds.Y;
         newBounds.Width = newBounds.Width;
         newBounds.Height = newBounds.Height + (int)delta.Y;
         break;
      case ResizeDirection.LeftBottom:
         newBounds.X = currentBounds.X + (int)delta.X;
         newBounds.Y = currentBounds.Y;
         newBounds.Width = currentBounds.Width + (int)delta.X * (-1);
         newBounds.Height = currentBounds.Height;
         newBounds.X = newBounds.X;
         newBounds.Y = newBounds.Y;
         newBounds.Width = newBounds.Width;
         newBounds.Height = newBounds.Height + (int)delta.Y;
         break;
      case ResizeDirection.None:
      default:
         break;
   }

   #endregion

   #region Handle mouse overflow

   float x = BorderBounds.X;
   float y = BorderBounds.Y;
   float w = BorderBounds.Width;
   float h = BorderBounds.Height;

   //
   // Look if the shape has at least its minimum size and prevent the shape
   // from resizing if the cursor overflowed the shape.
   //
   if (m_ResizeDirection == ResizeDirection.Right ||
       m_ResizeDirection == ResizeDirection.Bottom ||
       m_ResizeDirection == ResizeDirection.RightBottom)
   {
      if (newBounds.Size.Width >= MinSize.Width && m_MouseOverflowRightDown.X >= 0)
         w = newBounds.Width;
      else
         m_MouseOverflowRightDown.X += delta.X;

      if (newBounds.Size.Height >= MinSize.Height && m_MouseOverflowRightDown.Y >= 0)
         h = newBounds.Height;
      else
         m_MouseOverflowRightDown.Y += delta.Y;
   }

   if (m_ResizeDirection == ResizeDirection.Left ||
       m_ResizeDirection == ResizeDirection.Top ||
       m_ResizeDirection == ResizeDirection.LeftTop)
   {
      if (newBounds.Size.Width >= MinSize.Width && m_MouseOverflowLeftTop.X <= 0)
      {
         x = newBounds.X;
         w = newBounds.Width;
      }
      else
         m_MouseOverflowLeftTop.X += delta.X;

      if (newBounds.Size.Height >= MinSize.Height && m_MouseOverflowLeftTop.Y <= 0)
      {
         y = newBounds.Y;
         h = newBounds.Height;
      }
      else
         m_MouseOverflowLeftTop.Y += delta.Y;
   }

   if (m_ResizeDirection == ResizeDirection.LeftBottom)
   {
      if (newBounds.Size.Width >= MinSize.Width && m_MouseOverflowLeftBottom.X <= 0)
      {
         x = newBounds.X;
         w = newBounds.Width;
      }
      else
         m_MouseOverflowLeftBottom.X += delta.X;

      if (newBounds.Size.Height >= MinSize.Height && m_MouseOverflowLeftBottom.Y >= 0)
      {
         y = newBounds.Y;
         h = newBounds.Height;
      }
      else
         m_MouseOverflowLeftBottom.Y += delta.Y;
   }

   if (m_ResizeDirection == ResizeDirection.RightTop)
   {
      if (newBounds.Size.Width >= MinSize.Width && m_MouseOverflowRightTop.X >= 0)
      {
         w = newBounds.Width;
      }
      else
         m_MouseOverflowRightTop.X += delta.X;

      if (newBounds.Size.Height >= MinSize.Height && m_MouseOverflowRightTop.Y <= 0)
      {
         y = newBounds.Y;
         h = newBounds.Height;
      }
      else
         m_MouseOverflowRightTop.Y += delta.Y;
   }

   #endregion
}
//////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////
//
// This enum contains the different resize directions
//
[Flags()]
public enum ResizeDirection : byte
{
   /// <summary>
   /// No resize.
   /// </summary>
   None = 0,
   /// <summary>
   /// Resize to the left direction.
   /// </summary>
   Left = 2,
   /// <summary>
   /// Resize to the top direction.
   /// </summary>
   Top = 4,
   /// <summary>
   /// Resize to the right direction.
   /// </summary>
   Right = 8,
   /// <summary>
   /// Resize to the bottom direction.
   /// </summary>
   Bottom = 16,
   /// <summary>
   /// Resize to the left top direction.
   /// </summary>
   LeftTop = Left | Top,
   /// <summary>
   /// Resize to the right top direction.
   /// </summary>
   RightTop = Right | Top,
   /// <summary>
   /// Resize to the right bottom direction.
   /// </summary>
   RightBottom = Right | Bottom,
   /// <summary>
   /// Resize to the left bottom direction.
   /// </summary>
   LeftBottom = Left | Bottom
}
//////////////////////////////////////////////////////////////////////////////////////


The vector class is not really nessecary in this case but i was too lazy to replace it with Point-structures for example.
GeneralRe: resize and relocate by mouse Pin
Madmanxxx30-Mar-09 23:38
Madmanxxx30-Mar-09 23:38 
GeneralConnector Pin
Jian12345679-Jan-06 4:51
Jian12345679-Jan-06 4:51 
GeneralRe: Connector Pin
Sautin.net9-Jan-06 12:26
Sautin.net9-Jan-06 12:26 
GeneralRe: Connector Pin
Jian123456710-Jan-06 3:05
Jian123456710-Jan-06 3:05 
GeneralRe: Connector Pin
Sautin.net10-Jan-06 5:47
Sautin.net10-Jan-06 5:47 
GeneralRe: Connector Pin
Sautin.net10-Jan-06 5:48
Sautin.net10-Jan-06 5:48 
GeneralRe: Connector Pin
Jian123456710-Jan-06 6:59
Jian123456710-Jan-06 6:59 
GeneralRe: Connector Pin
Ariston Darmayuda5-Jul-07 7:29
Ariston Darmayuda5-Jul-07 7:29 
GeneralRe: Connector Pin
Sautin.net5-Jul-07 7:41
Sautin.net5-Jul-07 7:41 

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.