AutoCAD

Programming with Class, Part 1

15 Nov, 2004 By: Mike Tuersley Cadalyst

Don't be daunted by the often-unfamiliar yet very useful class module


For some reason, class modules seem daunting at first. Most VBA books brush over the topic, leaving it to the reader to discover. Which usually means the reader never looks very far into class modules and, therefore, doesn't take advantage of what classes have to offer. Let's change that over the next two months! This month is the first installment in a two-part series on programming with class modules. While the focus will be on VBA, VB programmers can benefit from this information as well.

A class, in layman's terms, is a user-defined programming object. This is not to be confused with a custom AutoCAD object! This programming object exists only within the program, not within the drawing. Class modules represent a vehicle to provide:

  • reusable/portable code
  • temporary data storage
  • simpler coding structure
Classes are just like internal VBA or AutoCAD objects: They can have properties, methods, and events. We will look at all of these over the next two months.

Getting Started with Class
The primary difference between class and standard modules is that all code within the class is related. Unlike standard modules where different subs/functions can perform different, unrelated tasks, all the code within the class must work toward the same goal. Therefore, it is said that the code contained in the class describes the object, or class itself.

To start off, here is a very simple, very basic example. Suppose I want to store all the information for a title block for access throughout my program. I could use separate global variables for each piece of data, or a UDT (user-defined data type), or an array, collection, or dictionary. Any of those choices would work except the global variables -- too many variables to keep track of! Instead of using one of the others, I'll use a class called TitleBlock. I'll create a new VBA project and add a class module to it. I'll name the class Titleblock and set its instancing to Private (figure 1). I'll explain instancing in Part 2.

figure
Figure 1. Naming the class module.

Within the class module, I'll type in:

Option Explicit

Public DwgTitle As String
Public DrawnBy As String
Public SheetNumber As String
I'll then add a standard module and add this code to it: Option Explicit
Sub Test()
  Dim oTB As TitleBlock
  Set oTB = New TitleBlock
  With oTB
    .DwgTitle = "FLOOR PLAN"
    .DrawnBy = "MTT"
    .SheetNumber = "A01"
  End With
End Sub
Notice that with a class, Dim and New are never used within the same line. This is due to how classes are initialized. Later I will explain how a class's Initialize Event can be used, but for now, just go along with me.

Adding Data to the TitleBlock
Now I am going to add data to my TitleBlock object. Notice that Intellisense works with the new oTB object by simply adding Public variables to the class module (figure 2). It's easier to get to the data in a class than an array or collection because I don't need to remember the position of the data within the array or collection.

figure
Figure 2. Intellisense working with the new TitleBlock object.

At this point, it would probably have been easier to use a UDT because classes are not really designed to function as demonstrated to this point. Consider how data is entered within a standard program. At every point where one of the three title block fields can be edited, there will need to be data validation. For example, I may need to check the sheet number to ensure it is in X-YYY format. To do so, I have three choices:

  • Generate code at each edit point and maintain each "set" of code.
  • Generate a sub/function to handle the data validation and make sure each edit point uses this sub/function.
  • Do the data validation "behind-the-scenes" within the class itself.
I think I'll go with the third option and place the data validation within the class module. Then I can make the validation automatic and will not need to worry about calling it every time or having numerous lines of code floating around that I will need to remember to update if needed. If I distribute this class for other programmers to use, I won't have to worry about them doing the data validation either.

Implementing Properties
To implement data validation, I cannot use the public variables because I have no control over someone editing them -- they are just public variables seen by any module within the current project. To control access to the data stored within my class, I need to use properties. Properties are variables that the class can control and can be of any data type -- string, integer, and so forth, including objects. We have three options for implementing properties:

  • Get: allows access to the property to retrieve its value
  • Let: allows access to the property to edit its value
  • Set: similar in function to LET, but used with objects
Okay, so now I am going to remove the public variables from my class module so I can start off with a blank slate. To add the skeleton for my properties, I'll use Insert / Procedure? from the pull-down menus (figure 3). In the Add Procedure dialog box, I will enter the property's name, select Property as the type and Public as the scope. This will then place the following code in my class module:
Public Property Get SheetNumber() As Variant
End Property

Public Property Let 
     SheetNumber(ByVal vNewValue As Variant)
End Property
figure

figure

Figure 3. Accessing the Procedure dialog box (left), and filling in the Add Procedure dialog box (right).

Now I need to do some editing:

  • Change SheetNumber from a Variant to a String.
  • Change the vNewValue parameter of the Let statement to a string.
  • Add a private variable for the class to use to store the property's value.
  • Set the value of the private variable in the Let statement.
  • Set the return value of the Get statement.
My class code would then look like:
Private sShtNum as String

Public Property Get SheetNumber() As String
  SheetNumber = sShtNum
End Property

Public Property Let SheetNumber(ByVal sNewValue As String)
  sShtNum = sNewValue
End Property
Let's review the Let and Get statements above. The Get statement is added so I can retrieve the value of the property any time I need it, like so:
Dim sDwgNum As String
sDwgNum = oTB.SheetNumber
If there is no value assigned, then sDwgNum will equal a vbnullstring [""], or I could assign a default value to it within the class module.

The Let statement is added, so I, or the programmer, can change the property's value. If not included, the property would be read-only.

Adding Data Validation
So far, so good. Now I need to add the data validation. I want the SheetNumber to be in the format: X-YYY, or A-001, A-022, and so forth. To accomplish this, I'll look back to last month's article on regular expressions and incorporate a regular expression to do the work:

Private sShtNum as String
Private oReg As RegExp

Public Property Get SheetNumber() As String
  SheetNumber = sShtNum
End Property

Public Property Let SheetNumber(ByVal sNewValue As String)
  If Not oReg.Test(sNewValue) Then
    Err.Raise vbObjectError + 513, "TitleBlock object", _ 
              "Expected value in the format of X-YYY"
  Else
    sShtNum = sNewValue
  End If
End Property

Private Sub Class_Initialize()
  Set oReg = New RegExp
  With oReg
    .IgnoreCase = False
    .Pattern = "[A-Z]-\d{3}"
  End With
End Sub

Private Sub Class_Terminate()
  If Not oReg Is Nothing Then Set oReg = Nothing
End Sub
Here, I have introduced the two major class events: Initialize and Terminate. Obviously, Initialize fires when the class is created or substantiated, and Terminate fires when all references to the class are destroyed. When the class initializes, I initialize the regular expression object and set up its properties. At terminate, I clear the regular expression object.

Within the Let statement, I test the value coming in to make sure it meets my criteria. If it does, I pass the incoming value to my property. If not, I raise an error so the programmer knows the value he is trying to set does not meet said criteria.

Well, that's enough for this month. Download the source code and see how I set up the other two properties. Next month I will wrap up the class.

Kick it UP a Notch!
In the past two articles, I have used regular expressions. To help generate the expressions, check out Expresso by UltraPico. This neat program lets you build, test, and have the code generated (in VB and C#) for them. Best of all, Expresso is free!


About the Author: Mike Tuersley


AutoCAD Tips!

Lynn Allen

Autodesk Technical Evangelist Lynn Allen guides you through a different AutoCAD feature in every edition of her popular "Circles and Lines" tutorial series. For even more AutoCAD how-to, check out Lynn's quick tips in the Cadalyst Video Gallery. Subscribe to Cadalyst's free Tips & Tools Weekly e-newsletter and we'll notify you every time a new video tip is published. All exclusively from Cadalyst!
Follow Lynn on Twitter Follow Lynn on Twitter


Poll
Which file format do you use most often for CAD drawing/model exchange?
Native format
PDF
3D PDF
DWF
STEP or IGES
JT
IFC
Other
Submit Vote