Creating a Shape Editor in .NET, Part 2: Creating The Shapes

Introduction

In the first installment of this article series, “Creating a Shape Editor in .NET,” we created the framework for our drawing application. With this installment, you will create the canvas to draw on and create the shapes. The functionality already exists for the shapes to be resized and dragged about.

There is a tonne of work, so let’s get started!

Create the line-snapping ability:

C#

   public class lnSnap
   {
      public lnSnap(int X1, int Y1, int X2, int Y2,
         Color LineColor)
      {
         this.X1 = X1;
         this.Y1 = Y1;
         this.X2 = X2;
         this.Y2 = Y2;

         this.Color = LineColor;
      }

      public int X1 { get; set; }
      public int Y1 { get; set; }
      public int X2 { get; set; }
      public int Y2 { get; set; }
      public Color Color { get; set; }

      public void Draw(Graphics g)
      {

         using (var p = new Pen(this.Color))
         {

            g.DrawLine(p, this.X1, this.Y1, this.X2, this.Y2);

         }

      }
   }

VB.NET

Imports System.Drawing
Public Class lnSnap
   Public Sub New(ByVal X1 As Integer, ByVal Y1 As Integer, _
         ByVal X2 As Integer, ByVal Y2 As Integer, _
         ByVal LineColor As Color)
      Me.X1 = X1
      Me.Y1 = Y1
      Me.X2 = X2
      Me.Y2 = Y2
      Me.Color = LineColor
   End Sub

   Public Property X1 As Integer
   Public Property Y1 As Integer
   Public Property X2 As Integer
   Public Property Y2 As Integer
   Public Property Color As Color

   Public Sub Draw(ByVal g As Graphics)
      Using p = New Pen(Me.Color)
         g.DrawLine(p, Me.X1, Me.Y1, Me.X2, Me.Y2)
      End Using
   End Sub
End Class

The lnSpan class gives us the ability to show different colored snapping lines when we move the drawings into each other’s vicinity. Add the following code:

C#

   public class shpCircle : shpShape
   {
      public shpCircle(Point loc) : base(loc)
      {

      }

      public override string ShapeName()
      {

         return "Circle";

      }

      public override void Draw(Graphics g)
      {

         using (var b = new SolidBrush(this.BackColor))
         {

            g.FillEllipse(b, this.Bounds);
            g.DrawEllipse(Pens.Black, this.Bounds);

         }

      }]

VB.NET

shpCircle.vb

Imports System.Drawing
Public Class shpCircle
   Inherits shpShape

   Public Sub New(ByVal loc As Point)
      MyBase.New(loc)
   End Sub

   Public Overrides Function ShapeName() As String
      Return "Circle"
   End Function

   Public Overrides Sub Draw(ByVal g As Graphics)
      Using b = New SolidBrush(Me.BackColor)
         g.FillEllipse(b, Me.Bounds)
         g.DrawEllipse(Pens.Black, Me.Bounds)
      End Using
   End Sub
End Class

This class creates a circle by using the DrawEllipse and FillEllipse methods. You obviously could add more shapes in here such as squares, triangles, and other polygons, but I’ll add that functionality perhaps a bit later. Add the next class.

C#

   public class shpColl : System.Collections.ObjectModel.Collection
      <shpShape>
   {

      private readonly shpCanvas canvas;
      public shpColl(shpCanvas c)
      {

         canvas = c;

      }

      public event EventHandler CollectionChanged;

      protected virtual void OnCollectionChanged(EventArgs e)
      {

         if (CollectionChanged != null) this.CollectionChanged
            (this, e);

      }

      private string FreeName(Type t)
      {

         if (t.IsSubclassOf(typeof(shpShape)))
         {
            var shapes = new List<shpShape>();

            foreach (shpShape s in this)
            {

               if (t == s.GetType())
               {

                  shapes.Add(s);

               }

            }

            var ht = new Hashtable(shapes.Count);

            foreach (shpShape s in shapes)

               ht[s.Name] = null;

            shpShape instance = (shpShape)Activator.CreateInstance
               (t, new object[] { Point.Empty });

            string defName = instance.ShapeName();

            int i = 1;

            while (ht.ContainsKey(defName + i))
               i++;

               return defName + i;

            }

         else
         {

            return String.Empty;

         }

      }

   }

VB.NET

Imports System.Drawing

Public Class shpColl
   Inherits System.Collections.ObjectModel.Collection(Of shpShape)

   Private ReadOnly canvas As shpCanvas

   Public Sub New(ByVal c As shpCanvas)
      canvas = c
   End Sub

   Public Event CollectionChanged As EventHandler

   Protected Overridable Sub OnCollectionChanged(ByVal e As _
         EventArgs)
      RaiseEvent CollectionChanged(Me, e)
   End Sub

   Private Function FreeName(ByVal t As Type) As String
      If t.IsSubclassOf(GetType(shpShape)) Then
         Dim shapes = New List(Of shpShape)()

         For Each s As shpShape In Me

            If t = s.[GetType]() Then
               shapes.Add(s)
            End If
         Next

         Dim ht = New Hashtable(shapes.Count)

         For Each s As shpShape In shapes
            ht(s.Name) = Nothing
         Next

         Dim instance As shpShape = CType(Activator. _
            CreateInstance(t, New Object() {Point.Empty}), _
            shpShape)
         Dim defName As String = instance.ShapeName()
         Dim i As Integer = 1

         While ht.ContainsKey(defName & i)
            i += 1
         End While

         Return defName & i
      Else
         Return String.Empty
      End If
   End Function
End Class

The aptly named shpColl class hosts a collection of shapes that have been drawn on the canvas. Now, speaking about the canvas…

Well, If I were to add the Canvas class’ code here in its entirety, it will take a very long time to read and use. I do not want you to just copy and paste the code and not understand it. That defeats the whole object of this article, all my articles, any other articles out there. Let me leave that task to a third and final part (which will be coming soon).

Conclusion

All we need now is a platform, or canvas, to draw our shapes on. This will be covered in the next and final installment. Until then, learn the code of this article, and the previous one. Experiment, toy and play with it, as that is how you learn.

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read