Using Visual Studio for Component Development

The objective of this tutorial is to demonstrate the process required to create a Visual Studio project such that you can develop Grasshopper components from this environment. While this mode of working requires certainly more elaborate initial preparation, it does pay back rather quickly because of the best in class editing capabilities as well as debugging and code modification while the application is running.

Project Structure

  1. Solution Generation: In Visual Studio, create a new C# class library project. Visual Studio will create for you a Solution folder and inside of it a Project folder with the name you specified (so you can host multiple projects within one solution).
  2. External References: In Windows Explorer, create a new folder inside the Solution folder named “References” and copy or hard-link inside this folder the  libraries seen below. This step is optional but simplifies software updates and/or synchronization between multiple computers through for example Dropbox, Google Drive or Windows One Drive.
    1. RhinoCommon.dll found in the system folder of your Rhino’s installation
    2. Grasshopper.dll and GH_IO.dll found in the folder of your Grasshopper installation.
    3. Jeneratiff.gha (rename to Jeneratiff.dll), Jeneratiff.gh8 and Jeneratiff.gh4 found in the installation folder.
  3. Import References: In Visual Studio / Project View, include all of the above libraries (right click on the References tree item) and in the Properties View (often seen in the panel right below) Turn Off local copy.
  4. User Settings: Create a text file in the Project folder named as “YourProjectName.csproj.user” and paste the following settings. This step is in order to ensure debugging works correctly. In the settings text file you need to replace the path with the location of your installation directory of Rhino’s main executable.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <ProjectView>ProjectFiles</ProjectView>
    <PublishUrlHistory />
    <InstallUrlHistory />
    <SupportUrlHistory />
    <UpdateUrlHistory />
    <BootstrapperUrlHistory />
    <ErrorReportUrlHistory />
    <FallbackCulture>en-US</FallbackCulture>
    <VerifyUploadedFiles>false</VerifyUploadedFiles>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
    <StartProgram>C:\Home\Apps\Rhino5\System\rhino4.exe</StartProgram>
    <StartAction>Program</StartAction>
  </PropertyGroup>
</Project>

Project Source Code

#region -- References ---------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;

using Gha;
using Rhino.Geometry;

[assembly: AssemblyTitle( "Tutorial" )]
[assembly: AssemblyDescription( "Digital Design and Fabrication Tutorial" )]
[assembly: AssemblyConfiguration( "Alpha" )]
[assembly: AssemblyCompany( "DDF Pte Ltd" )]
[assembly: AssemblyProduct( "Tutorial" )]
[assembly: AssemblyCopyright( "Copyright DDF © 2016" )]
[assembly: AssemblyTrademark( "DDF" )]
[assembly: AssemblyCulture( "" )]
[assembly: ComVisible( false )]
[assembly: Guid( "6a4d2348-2e7d-40b3-9ab4-80067f6c1a36" )]
[assembly: AssemblyVersion( "1.0.0.0" )]
[assembly: AssemblyFileVersion( "1.0.0.0" )]

#endregion

namespace DigitalDesignAndFabrication
{
  public class HackingTemplateJeneratiffStyle : Part
  {
    #region -- Construction -------------------------------------------------

    public List<string>  Messages;
    public List<object>  Geometry;
    public List<Point3d> AnnoPnts;
    public List<string>  AnnoTxts;

    public HackingTemplateJeneratiffStyle( ) : base( Inherited: true )
    {
      //-- The first parameter is the long name of the component
      //-- the second parameter is the short name and the text
      //-- that renders as the icon of the component. You must
      //-- use 3 characters \r\n then another three characters
      //-- otherwise it wont fit in the icon. Then we have the
      //-- the tab category and sorting order 
      //--
      Register( "The Hack", "THE\r\nHCK", In.Develop, Of.G7 );
    }

    #endregion

    #region -- Utility ------------------------------------------------------

    public void Annotate( Point3d point, string message, params object[] args )
    {
      AnnoTxts.Add( string.Format( message, args ) );
      AnnoPnts.Add( point );
    }

    public void Print( string message, params object[] args )
    {
      Messages.Add( string.Format( message, args ) );
    }

    #endregion

    #region -- Registration -------------------------------------------------

    public override void Register( Part.IO request )
    {
      //-- Add / Remove In/Out Parameters
      //-- If you change this during debugging you need to delete
      //-- the existing component and place a new one to force update
      //--
      if( request == IO.Load )
        Register( "Param 0",  As.Obj, By.Item ).
        Register( "Param 1",  As.Obj, By.Item ); else
        Register( "Messages", As.Str, By.List ).
        Register( "Geometry", As.Obj, By.List ).
        Register( "AnnoPnts", As.Obj, By.List ).
        Register( "AnnoTxts", As.Str, By.List );
    }

    #endregion

    #region -- Solution -----------------------------------------------------

    public override bool Solve( )
    {
      //-- SOP Initialize
      //--
      Messages = new List<string>( );
      Geometry = new List<object>( );
      AnnoPnts = new List<Point3d>( );
      AnnoTxts = new List<string>( );

      //-- Add you code here
      //--
      Print( "Value: {0}", 1 );
      Geometry.Add( new Point3d( 0, 0, 0 ) );
      Annotate( new Point3d( 2, 3, 4 ), "Hello" );

      //-- SOP Compete
      //--
      SetLst( 0, Messages );
      SetLst( 1, Geometry );
      SetLst( 2, AnnoPnts );
      SetLst( 3, AnnoTxts );

      return true;
    }

    #endregion
  }

  #region -- Grasshopper Style Component --------------------------------------

  //-- Provided for reference
  //--
  public class HackingTemplateGrasshopperStyle : Grasshopper.Kernel.GH_Component
  {
    protected override void RegisterInputParams(
      Grasshopper.Kernel.GH_Component.GH_InputParamManager data )
    {
      data.AddTextParameter( "String", "S", "Text In",
        Grasshopper.Kernel.GH_ParamAccess.item );
    }

    protected override void RegisterOutputParams(
      Grasshopper.Kernel.GH_Component.GH_OutputParamManager data )
    {
      data.AddTextParameter( "String", "S", "Text Out",
        Grasshopper.Kernel.GH_ParamAccess.item );
    }

    protected override void SolveInstance(
      Grasshopper.Kernel.IGH_DataAccess data )
    {
      var text = string.Empty;
      data.GetData( 0, ref text );
      data.SetData( 0, text );
    }

    public override Guid ComponentGuid
    {
      get
      {
        return new Guid( "419c3a3a-cc4e-4717-8cef-5f5647a5ecfc" );
      }
    }
  }

  #endregion
}