Click or drag to resize

Simple Component (VB)

This article contains an exhaustive, step by step explanation of how to build your first component in VB.NET. It will skip over any complicated issues (such as mathematics, geometry and data handling) in order to reduce the totality of new concepts. You will however need to have a good understanding of basic OOP concepts such as classes, types and inheritance. If you do not understand these DotNET essentials, we recommend you start with some other reading material first.

Grasshopper.Kernel.GH_Component

All you need to do in order to define a new Component is to inherit from the GH_Component base class in Grasshopper. Assuming you've set up your project correctly, create a new, blank class in your project:

At this point a new file should be created (MyFirstComponent.vb) with the following content:

VB
Public Class MyFirstComponent

End Class
Since we'll be using primarily types from the Grasshopper.Kernel namespace. We'll import this namespace so that we have easy access to all types contained within it (unless otherwise specified, all further types discussed in this topic belong to Grasshopper.Kernel):
VB
Imports Grasshopper.Kernel
Public Class MyFirstComponent

End Class
Now, we need to derive our MyFirstComponent class from the GH_Component base class defined inside Grasshopper. GH_Component takes care of almost all the complicated actions and mannerisms that constitute a Component. It will handle data conversion, GUI, menus, file Input/Output, Error trapping and much, much more. This allows us to focus only on the unique aspects of our Component. In order to derive from GH_Component, add the following line directly underneath the class declarator:
VB
Imports Grasshopper.Kernel
Public Class MyFirstComponent
  Inherits GH_Component

End Class
As soon as you press Enter after finishing that line, Visual Studio will insert a bunch of empty properties and subroutines which we are required to fill out, but we'll deal with that in the next sections:
VB
Imports Grasshopper.Kernel
Public Class MyFirstComponent
  Inherits GH_Component

  Protected Overrides Sub RegisterInputParams(ByVal pManager As GH_Component.GH_InputParamManager)
    'Don't worry about this just yet!
  End Sub

  Protected Overrides Sub RegisterOutputParams(ByVal pManager As GH_Component.GH_OutputParamManager)
    'We'll get to it soon enough.
  End Sub

  Protected Overrides Sub SolveInstance(ByVal DA As IGH_DataAccess)
    'I know it all looks scary.
  End Sub

  Public Overrides ReadOnly Property ComponentGuid() As System.Guid
    Get
      'But we'll deal with it one item at a time.  
    End Get
  End Property
End Class

The Component Constructor

As we've seen in the previous section, Visual Studio will populate the MyFirstComponent class with a collection of properties and subroutines that we need to implement. There is however another subroutine that requires our attention that is missing. This is the constructor. The constructor is a special subroutine inside each class which gets called when the class is instantiated (or "constructed"). This can happen only once (we feeble humans can only be born once as well after all) and it necessarily happens before anything else is allowed to happen. The GH_Component base class has a constructor which is not empty, so we have to call that constructor from within our constructor and supply it with all the information it needs. Let's play ball. Add the following code near the top of the MyFirstComponent class:

VB
Imports Grasshopper.Kernel
Public Class MyFirstComponent
  Inherits GH_Component

  Public Sub New()
    MyBase.New("MyFirst", "MFC", "My first component", "Extra", "Simple")
  End Sub

  ...
As you can see we need to supply a set of text constants, which are used to name and identify our component within the Grasshopper GUI. The text fields are:

ParameterPurpose
nameThe name of our component. The name is what appears on tooltips and Panel dropdowns.
abbreviationThe abbreviation of our component. The abbreviation is what is written on the component once it appears on the Canvas.
descriptionA description of our component. The description is used on tooltips to provide users with a more detailed idea about what this component is for.
categoryThe tab category for the component. The category equals the name of the Tab onto which the Component will appear. If a non-existing category is supplied, a new Tab will be added to the Grasshopper GUI.
subCategoryThe panel category for the component. The sub-category equals the name of the Panel onto which the Component will appear. If a non-existing sub-category is supplied, a new Panel will be added to the Category Tab.

Component Guids

Every type of object inside a Grasshopper document must have a Guid associated with it. When a Grasshopper file (*.gh or *.ghx) is written these Guids are used as markers, so it becomes clear what portions of the file belong to which object. When the file is read back in, that marker is compared against the list of all cached components and if a match is found the appropriate component is asked to please go and deserialize itself. (You thought I was going to say something else didn't you?). When no matching component can be found it is assumed that whoever wrote the file had access to certain components that are not available locally, and that portion of the file is dutifully skipped.

So, long story short, we need to invent a Guid (Globally Unique IDentifier) that will positively and unerringly indicate this component. You can generate spanking new Guids using an Online Guid Generator or Microsofts popular guidgen.exe. Never re-use a Guid and never edit one by hand. Always generate a proper one using an official tool.

Once you have a new Guid standing by, modify the ComponentGuid property to return it:

VB
...
Public Overrides ReadOnly Property ComponentGuid() As System.Guid
  Get
    'Don't copy this GUID, make a new one
    Return New Guid("419c3a3a-cc48-4717-8cef-5f5647a5ecfc")  
  End Get
End Property
...

Parameter Registration

Components have unique input and output parameters which are most often fixed. We are ignoring those rare cases where a component either has no inputs or no outputs, or where there is a variable number of parameters. There are two subroutines that allow you to define (or "register") these parameters. These routines are called from within the base class constructor and they are only called once. Let's have a look at the default implementation that Visual Studio generated again:

VB
...
Protected Overrides Sub RegisterInputParams(ByVal pManager As GH_Component.GH_InputParamManager)

End Sub

Protected Overrides Sub RegisterOutputParams(ByVal pManager As GH_Component.GH_OutputParamManager)

End Sub
...
Although it would technically be possible to manually register parameters, we highly recommend you use the methods on pManager. pManager has methods for adding all the basic parameter types and it often even allows you to specify default values:

In this example we'll only create two parameters (one input, one output) and they will both be of type String.

VB
...
Protected Overrides Sub RegisterInputParams(ByVal pManager As Grasshopper.Kernel.GH_Component.GH_InputParamManager)
  pManager.AddTextParameter("String", "S", "String to reverse")
End Sub

Protected Overrides Sub RegisterOutputParams(ByVal pManager As Grasshopper.Kernel.GH_Component.GH_OutputParamManager)
  pManager.AddTextParameter("Reverse", "R", "Reversed string")
End Sub
...
When we compile this project (assuming it has been setup correctly), the component will already be available on the Grasshopper tabs and it can be placed onto the canvas:

The Solver Routine

Our new component sure looks perky and expensive, but it doesn't do anything useful yet (am I the only one who's reminded of Paris Hilton?). We still need to write the contents of the SolveInstance subroutine, which is where all the action takes place. The SolveInstance() function is called upon whenever the component needs to handle input data. In this particular example, if we plug a list of twelve Strings into the [S] parameter, SolveInstance() will be called twelve times.

As you may already have guessed, the component we're writing will reverse a given textual string from [S] and output the result to [R]. Since we're operating on individual items of data (the default behaviour), all we need to do inside the SolveInstance() function is retrieve the current String from [S], reverse it and assign it to [R]. Now, String reversal is not a function that is directly available in the framework String type, so we need to actually do some thinking:

VB
...
Protected Overrides Sub SolveInstance(ByVal DA As Grasshopper.Kernel.IGH_DataAccess)
  'Declare a variable for the input String
  Dim data As String = Nothing

  'Use the DA object to retrieve the data inside the first input parameter.
  'If the retieval fails (for example if there is no data) we need to abort.
  If (Not DA.GetData(0, data)) Then Return

  'If the retrieved data is Nothing, we need to abort.
  'We're also going to abort on a zero-length String.
  If (data Is Nothing) Then Return
  If (data.Length = 0) Then Return

  'Convert the String to a character array.
  Dim chars As Char() = data.ToCharArray()

  'Reverse the array of character.
  Array.Reverse(chars)

  'Use the DA object to assign a new String to the first output parameter.
  DA.SetData(0, New String(chars))
End Sub
...