Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

ModelStudio

3.20/5 (4 votes)
6 Dec 2008CPOL4 min read 35.1K   386  
Class diagram and code generation tool.

Sample screens

Introduction

Class diagrams are important in developing a model for a software system before you actually start programming your software. They provide designers, architects, and developers a graphical interface to manipulate methods, properties, fields, events for data structures like classes, interfaces, structs, and more. In this article, we will use what I call ModelStudio as a designer to help you create your model structures before directly diving into the code, and while we are interacting with this designer, it should be noted that the code will be updated in the background and later auto-generated using the System.CodeDom namespace.

Background

I think that modeling your code first is a good first step in OOP and designing a great software application architecture. If you are looking for a Reflective solution, then you may want to take a look at something like Sacha Barber's 100% Reflective Class Diagram Creation Tool. For the diagrammer, which I won't discuss in this article, I modified and used the ClassDrawerContainerPanel and dependent classes from Barber's tool. I also want to acknowledge the use of Chagosoft's cDOMx Class Builder for ideas as well.

Using ModelStudio

The ModelStudio application uses the ModelStudio Framework as we refer to create Visual Basic and C# code documents. To use ModelStudio is fairly straightforward.

  1. When you start the program, you will be presented with a blank canvas.
  2. To start creating your first class, simply click the "New Namespace" button, give it a name, and a new Namespace object is created.
  3. Then, simply select the namespace from the left to add a "New Import" or "New Class".
  4. To add properties and methods to a class, simply select the required class and use the "New Property" or "New Method" buttons. To add parameters to methods is very simple as well; simply select the method from the left and click "New Parameter".
  5. When you are happy with your project, you have these options for getting the code:
    1. Use the "Generate Code" button to retrieve the code of the project in your desired language.
    2. Use the "Generate Files" button to generate the individual class documents in the selected folder in the desired language.
    3. You also have the option to save the project (Repository) as a file for future editing or transport using the "Save" button.
  6. To open a project file, use the "Open" button, browse to the file, and the project will be loaded.

Purpose

There is one note that I need to add about my purpose for putting this resource tool together. Starting with Visual Studio 2005, Microsoft has included what they call the Class Designer. The Class Designer tool may work for your organization, especially if you have the super duper Visual Studio Team System. However, what I dislike about the Class Designer tool are the 'little things'. For instance, I like for all generated classes to initialize any custom types during class construction.

VB
Books = New List(of Book)

Class Designer neglects to do that. Secondly, wouldn't it be nice to have completed setters and getters for all your properties along with the associated fields auto declared? Class Designer neglects to do this as well. Instead, what Class Designer provides you with is only a prototyped class property by default.

Prototype
VB
Public Overridable Property Name() As String
    Get

    End Get
    Set(ByVal value As String)

    End Set
End Property
Ideal
VB
Private m_Name As String

Public Overridable Property Name() As String
    Get
        Return Me.m_Name
    End Get
    Set(ByVal value As String)
        Me.m_Name = value
    End Set
End Property

The Framework

The ModelStudio Framework implements a simple design that first encapsulates the class information into a 'facade' of simple classes. Here we use the term facade in its most liberal sense. Definition: "a superficial appearance or illusion of something. (E.g.: They managed somehow to maintain a facade of wealth)". These facade classes and the data within each is then "translated", which we will discuss later, to create the actual CodeDom classes for code generation.

ModelStudio Framework

Facade

CodeDomImport

Description: This is an example facade class that will be used to create a new Imports (i.e., using) statement for your generated classes.

VB
Public Class CodeDomImport
    Implements ICodeMember

    Private m_Import As String = "System.Text"
    Private m_Parent As Object

    ''' Initialize a new instance of the CodeDomImport object
    ''' The fully qualified Namespace that will be imported ie: System.Text
    Sub New(ByVal ImportName As String)
        m_Import = ImportName
    End Sub

    ''' The fully qualified Namespace that represents the import ie: System.text
    Public Property Name() As String Implements ICodeMember.Name
        Get
            Return m_Import
        End Get
        Set(ByVal value As String)
            m_Import = value
        End Set
    End Property

    Public Property Parent() As Object
        Get
            Return m_Parent
        End Get
        Set(ByVal value As Object)
            m_Parent= value
        End Set
    End Property

End Class

CodeGenerator Class

This is the business end of the whole thing. Here is where the actual code gets translated, prepared, and generated to code files.

Basically, the structure of this class is not ideal. However, for the purposes of this article, everything gets done inside this one class. Translation takes place when we return a regular CodeDOM CodeNamespace.

Translation

VB
Public Function GetNamespace(ByVal _Namespace As CodeDomNamespace) As CodeNamespace
    Dim _space As CodeDomNamespace = _Namespace
    Dim _ns As CodeNamespace = _Namespace.DOMNameSpace

    '==Imports
    If _space.Imports.Count > 0 Then
        For Each import As CodeDomImport In _space.Imports
            Dim im As New CodeNamespaceImport(import.Name)
            _ns.Imports.Add(im)
        Next
    End If
....
End Function

Preparation

Now we need to prepare the code for output the way we want it. During what we call the preparation stage, you will be using the following CodeDom namespaces:

VB
Imports System.CodeDom
Imports System.CodeDom.Compiler

To prepare classes to initialize all custom types during class construction, we use the following code:

VB
Dim mcdClass As CodeTypeDeclaration = _Class.DOMClass
'Constructors
For Each meth As CodeDomMethod In _Class.Constructors
    If meth.Parameters.Count > 0 Or _Class.CustomTypes.Count > 0 Then
        Dim cnstr As New CodeDom.CodeConstructor

        For Each param As CodeDomParameter In meth.Parameters
            cnstr.Parameters.Add(New _
                  CodeParameterDeclarationExpression(param.Type, param.Name))
        Next
        
        ' Here is where custom initialization begins
        For Each p As CodeDomProperty In _Class.CustomTypes
            Dim this As New CodeThisReferenceExpression()
            Dim leftExpression As New CodePropertyReferenceExpression(this, p.Name)
            Dim rightExpression As New _
                CodeObjectCreateExpression(New CodeTypeReference(p.Type))

            Dim assign As New CodeAssignStatement(leftExpression, rightExpression)

            cnstr.Statements.Add(assign)
        Next p

        mcdClass.Members.Add(cnstr)
    End If
Next meth

Now, you can change your constructor to initialize any type that you want by simply adjusting the above commands to meet your own individual preferences.

Generation

Here is where we actually output what we've translated and prepared. Obviously, we are going to generate VB code here, but there is also a similar C# generation method as well.

VB
Private Sub GenerateVBFile(ByVal n As CodeNamespace, ByVal OutputDirectory As String)
        m_Unit = New CodeCompileUnit
    m_Unit.Namespaces.Add(n)
    ' Generate the code with the VB code provider.
    Dim provider As Microsoft.VisualBasic.VBCodeProvider = _
        New Microsoft.VisualBasic.VBCodeProvider
    ' Build the output file name.

    For Each c As CodeDom.CodeTypeDeclaration In n.Types
        Dim tempUnit As New CodeCompileUnit
        tempUnit.Namespaces.Add(New CodeDom.CodeNamespace(n.Name))
        tempUnit.Namespaces(0).Types.Add(c)

        ' Build the output file name.
        Dim sourceFile As String = OutputDirectory + _
         Path.DirectorySeparatorChar + c.Name + "." + provider.FileExtension

        ' Create a TextWriter to a StreamWriter to an output file.
        Using tw As New IndentedTextWriter(New StreamWriter(sourceFile, False), "    ")

            ' Generate source code using the code provider.
            provider.GenerateCodeFromCompileUnit(tempUnit, tw, New CodeGeneratorOptions())

            ' Close the output file.
            tw.Close()
        End Using
    Next c

End Sub

Pretty simple, just sets up the private members and calls GenerateCodeFromCompileUnit from the VBCodeProvider and spits out a class, which should look like:

VB
Namespace Classroom
    
Public Class Student

    Private m_Name As String
    Private m_StudentID As Integer
    Private m_MyTeacher As Teacher
    Private m_MyChair As Chair
    Private m_IsSpecial As Boolean
    Private m_Books As List(Of Book)

    Private Sub New()
        MyBase.New
        Me.MyTeacher = New Teacher
        Me.MyChair = New Chair
        Me.Books = New List(Of Book)
    End Sub

    Public Overridable Property Name() As [String]
        Get
            Return Me.m_Name
        End Get
        Set
            Me.m_Name = value
        End Set
    End Property
    ...
End Class
End Namespace

Wrap Up

The last part we have to do is import all of the generated classes into our project solution. Build it and run it. Congratulations, now the real work begins.

License

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