Implementing Event Listening for Custom Grasshopper Components

Hi,

I want to listen for events from custom Grasshopper components in my application.

E.g. a component with name MyComponent is placed on the grasshopper canvas, then the parameter X (Int Value) has changed.

Now I would like to be notified that the parameter X of the component MyComponent has changed and receive the value of the changed parameter.

Can anyone help me how I can do this? :slightly_smiling_face:

using System;
using System.Threading.Tasks;
using Rhino.Runtime.InProcess;

namespace RhinoGrasshopper
{
  static class Program
  {
    static Program()
    {
      RhinoInside.Resolver.Initialize();
    }

    [STAThread]
    static void Main()
    {

      using (var rhinoCore =
               new Rhino.Runtime.InProcess.RhinoCore(new string[] { "/NOSPLASH" }, WindowStyle.Normal))
      {
        if (LaunchGrasshopper())
        {
          Console.WriteLine(@"Grasshopper launched");
          Task.Run(() => ListenOnComponentChange());
        }

        Rhino.RhinoApp.MainLoop += (s, e) => { if (Rhino.RhinoApp.IsClosing) Console.WriteLine(@"closing rhino"); };

        rhinoCore.Run();
      }
    }

    private static bool LaunchGrasshopper()
    {
      var grasshopperInterface = Rhino.RhinoApp.GetPlugInObject("Grasshopper") as Grasshopper.Plugin.GH_RhinoScriptInterface;
      grasshopperInterface?.EnableBanner();
      grasshopperInterface?.ShowEditor();

      return grasshopperInterface != null;
    }

    private static void ListenOnComponentChange()
    {
      // Loop....
     // Component param has changed...
    }

  }
}



Hi Mr. Brunner,

I am not particularly familiar with grasshopper custom components. But could this not be solved by a standard event pattern?

Best,
Chen

1 Like

Hey,

As chen said, you would need to look into events.
The below will notify you when a new input is plugged into the X coordinate of a Construct Point, but then you would need to do some further work to subscribe to changes to that input and retrieve the value.

        string _componentName = "Construct Point";
        string _parameterName = "X coordinate";

        private void SubscribeToComponentsAdded()
        {
            var doc = OnPingDocument(); // this gets the document in grasshopper, you probably need to do it differently with Rhino.Inside
            if (doc == null) return;

            doc.ObjectsAdded += Doc_ObjectsAdded;
        }

        private void Doc_ObjectsAdded(object sender, GH_DocObjectEventArgs e)
        {
            if (string.IsNullOrEmpty(_componentName)) return;
            var matchingComponents = e.Objects
                .OfType<IGH_Component>()
                .Where(obj => obj.Name == _componentName);

            foreach (var component in matchingComponents)
            {
                component.Params.ParameterChanged += Params_ParameterChanged;
            }
        }

        private void Params_ParameterChanged(object sender, GH_ParamServerEventArgs e)
        {
            if (_parameterName == null) return;

            if (e.Parameter.Name != _parameterName) return;

            DoSomething();
        }

Hope that helps a little

2 Likes

Hi Chen and dchristev

Thank you both for the hints.

I want to use an observer for my approach using the C# delegates / events. The hint with doc.ObjectsAdded += Doc_ObjectsAdded; has already helped me therefore.

I am now taking a brute force approach to wait for the GH_Document. As soon as this is instanciated i subscribe on ObjectsAdded.
In a further step, I subscribe to my own components.

...
 if (_grasshopperInterface.IsEditorLoaded())
 {
   Task.Run(() => SubscribeToComponentsAdded());
 }
....
  private static void SubscribeToComponentsAdded()
  {
    GH_Document doc = null;
    while (doc == null)
    {
     doc = Instances.ActiveCanvas.Document;
    }

    doc.ObjectsAdded += (s, e) =>
    {
      // do something
    };
  }
}