Wednesday, April 6, 2022

7-How to create multiple components

This post discusses how to change the software architecture of the Test Diagram project to support the creation of multiple components of different types with different graphical representations. For example, we would like to create rectangles, circles, diamonds, triangles, etc. The form Paint event handler loops throught the list of components and calls the component Draw() method. How can we change the system so that the foreach loop will call the correct Draw() method for any shape or component type?

Time to use Inheritance and Polymorphism. Poly-What-ism? Polymorphism is a fancy OOP word for classes that call methods based on the class type inherited from a base class. There is some magic involved in this process but it works very well. We need to do the following:

  • Define a base class for all components
  • Create Virtual methods for all methods that are called in iterators - Draw()
  • Create a class for each component which inherits from the base class
  • Create a Draw() method that "overrides" the base class virtual method
  • Create menu buttons for each component type
  • Create menu button handlers to create the associated component and add it to the component list
The current Comp.cs class will be the base class for specific component classes. Add the virtual keyword to the Draw() method in Comp.cs as follows:

public virtual void Draw(Graphics gr)
{
    // Draw a simple rectangle with black border and no fill color
    gr.DrawRectangle(pen, 100, 100, 100, 100);
}

The virtual method does not need to have any code in the body. This implies that the method must be defined by any subclasses. If we leave the code, we can add a generic Comp to the component list and the Paint event will draw a rectangle.

Next, define classes for Rectangle, Triangle, and Circle that inherit Comp as the base class. Note that the pen attribute is inherited by each subclass also but the access modifier needs to be set to "protected".

For example, here is the Rectangle class:

using System;
using System.Collections.Generic;
using System.Drawing;
 
namespace TestDiagram
{
    class Rectangle : Comp
    {
        public Rectangle()
        {
 
        }
 
        public override void Draw(Graphics gr)
        {
            // Draw a simple rectangle with black border and no fill color
            gr.DrawRectangle(pen, 250, 100, 100, 100);
        }
    }
}

Note that Rectangle inherits from Comp and that the Draw() method uses the override keyword for the base class virtual method.

Create a new button:
  • Text: Rectangle
  • Name: btnRectangle
Double click on the button to create a Click event handler:

private void btnRectangle_Click(object sender, EventArgs e)
{
    Rectangle rect = new Rectangle();
    comps.Add(rect);
    schematicCanvas.Invalidate();
}

Note that the subclass Rectangle is used to instantiate the Rectangle component and is added to the form component list. Run the program and ensure that a new rectangle is created when the Resistor button is clicked.

Follow the same procedure to create a circle and triangle class. The Draw() methods are shown below:

Circle

public override void Draw(Graphics gr)
{
    // Draw a simple rectangle with black border and no fill color
    gr.DrawEllipse(pen, 400, 100, 100, 100);
}


Triangle - Wait. The Microsoft graphics library does not contain a definition for Triangles. We will create one using lines. This will be the first introduction to drawing lines, it will not be the last.

using System;
using System.Collections.Generic;
using System.Drawing;
 
namespace TestDiagram
{
    class Triangle : Comp
    {
        // Define the points for the upper left corner of the shape
        int locX = 550;
        int locY = 100;
 
        // Define 3 point attributes for the triangle
        Point p1, p2, p3;
 
        public Triangle()
        {
            // Initialize the points to the position of each corner of the triangle
            p1 = new Point(locX, locY + 100);
            p2 = new Point(locX + 100, locY + 100);
            p3 = new Point(locX + 50, locY);
        }
 
        public override void Draw(Graphics gr)
        {
            // Draw a simple triangle with black border and no fill color
            gr.DrawLine(pen, p1, p2);
            gr.DrawLine(pen, p2, p3);
            gr.DrawLine(pen, p3, p1);
        }
    }
}

We simply define the location of the upper-left corner of the shape as integers. Then we define the 3 points of the triangle relative to the location points. In the Draw() method, we draw 3 lines between the 3 points to form a triangle. All of the component classes will require a custom Draw() method using primitive lines, rectangles, and arcs to form the component graphics. See GitHub commit #6 for the full source code for this post.



No comments:

Post a Comment

34-Microwave Tools with Analysis (Series Final Post)

In this final blog post, I have integrated Y-Matrix analysis into the Microwave Tools project. This version has addition Lumped, Ideal, Micr...