How to prevent multiple instances of a Grasshopper component being added to the canvas

Grasshopper components typically have the ability to be added to the canvas multiple times to allow different calculations to be performed depending on what it is your Grasshopper document is doing. However, there may occasionally be a need for your component to only be allowed on the canvas once. There could be many reasons for this, such as a DLL being used which may conflict if loaded twice.

Whatever the reason, ensuring only one instance of your Grasshopper component is added to the canvas is a surprisingly easy task, but very little appears on a Google search of the topic. Hopefully this post will fill that gap and allow you to also prevent multiple instances of your component if needed.

Step 1 – Understanding our Guids

Each component has two Guid identifiers which are used to track them, the first is a component Guid, which is unique to each component and identifies that component in the object tree. The second is an instance Guid, which is unique to that instance of the component, which allows us to keep track of each instance of the component.

In each component you create, there will be a function called ‘ComponentGuid’ which is returning the component Guid for that component. It will look something like this:

public override Guid ComponentGuid
{
    get { return new Guid("{f53beca1-fc1e-421e-85aa-a1a4a45fbc61}"); }
}

The text passed as to the constructor of the Guid will be difference in every component you make, the chances of two components having an identical Guid are very slim indeed.

Step 2 – Override the ‘AddedToDocument’ method

In the background of a component there is an ‘AddedToDocument’ function which performs some actions after the component is successfully added to the document. In order to add our own functionality to this, we need to override it, by inserting the following code somewhere in our component class.

public override void AddedToDocument(GH_Document document)
{
    base.AddedToDocument(document);
}

The ‘base.AddedToDocument’ line allows the background code to be run as well, either before or after our additional code has run (depending on where we put our code).

Step 3 – identify whether this component already exists

Before we prevent this component from being added, we need to check if it already exists on the canvas. If it doesn’t, then we want this instance to be added, but if it does we want to remove it. We can find this out by inspecting the object table which resides in the active document. We can use a for each loop to loop through all of the components to see if we have a match, like so:

public override void AddedToDocument(GH_Document document)
{
    foreach(IGH_DocumentObject obj in Grasshopper.Instances.ActiveCanvas.Document.Objects)
    {
        //Looping through the objects in the active canvas document
    }

    base.AddedToDocument(document);
}

IGH_DocumentObject provides us with some methods and properties which we can use to ascertain whether our component already exists in the object table. The key ones we’re focusing on are the Instance Guid and Component Guid. We can compare these with our component to see if they match. If they do match, we can assume that our component already has an instance on the canvas. This is implemented as one if statement seen below. We can break it down into multiple, but the code is cleaner as one statement, as both parts must be true for us to assume our component already exists and we do not have any code to run for the ‘else’ statement in this instance.

public override void AddedToDocument(GH_Document document)
{
    foreach(IGH_DocumentObject obj in Grasshopper.Instances.ActiveCanvas.Document.Objects)
    {
        //Looping through the objects in the active canvas document
        if( (obj.ComponentGuid == this.ComponentGuid) && (obj.InstanceGuid != this.InstanceGuid) )
        {
            //If the component Guid matches and if the instance Guids are different...
        }
    }

    base.AddedToDocument(document);
}

Why do we check the component Guid and instance Guid?

If you don’t care why we do this check (and are just looking for the rest of the code to implement it yourself), skip to step four.

We check the component Guids to ensure that the component we are adding is of the same type as a component on the canvas. As mentioned in step one, the chances of two different components having identical Guids are highly remote. So in our first check, if the component we’re checking against is not of the same type (i.e. the Guids don’t match) as the component we’re adding, we can skip to the next object quite happily.

We then check the instance Guids to ensure we’re not comparing the component we’re adding with itself. By the time we reach the ‘AddedToDocument’ method, the component has already been added to the canvas and the object table, but it is unlikely to have been drawn yet. But it exists within the object table which we’re looping through. So if we’ve found that the component we’re adding shares its Guid with a component in the table, we need to make sure it’s not simply this instance. This is to ensure we can add the component at least once. If we didn’t compare the instance Guids, then when we add our component for the first time, it would remove itself simply because the component Guids match. If this is the only component of its type on the canvas, then checking if the instance Guids match ensures we can add it to the canvas. When we next come through this code (when we’re trying to add a second instance of the component), this helps ensure we only remove the new addition and not the existing component.

Step 4 – Removing our additional component instance

If our if statement returns true (i.e. the component Guids match, but the instance Guid’s are different), then we need to remove this new instance from the canvas. To do this, we can call the ‘RemoveObject’ method on the active canvas document, like so:

public override void AddedToDocument(GH_Document document)
{
    foreach(IGH_DocumentObject obj in Grasshopper.Instances.ActiveCanvas.Document.Objects)
    {
        //Looping through the objects in the active canvas document
        if( (obj.ComponentGuid == this.ComponentGuid) && (obj.InstanceGuid != this.InstanceGuid) )
        {
            //If the component Guid matches and if the instance Guids are different...
            Grasshopper.Instances.ActiveCanvas.Document.RemoveObject(this, false); //True or false - do we want to recompute the canvas? We're removing a component that was unnecessary, so I'm setting this to false - we don't need to recompute the canvas
            break; //We found and removed our additional instance, we don't need to continue looping (saves computational time and prevents a Grasshopper complaint that the collection (Document.Objects) was modified when you're trying to use it!
        }
    }

    base.AddedToDocument(document);
}

This will remove the additional instance of our component from the canvas, leaving us with only the one to play with (the original one). However, the user won’t know what’s happened. As far as they’re concerned, they’ve clicked to add a component and it hasn’t appeared. So it’s good practice to inform the user that you’re vetoing their decision to add a second instance of your component. This can be done by using a Windows Forms pop-up box. I would use it like this:

public override void AddedToDocument(GH_Document document)
{
    foreach(IGH_DocumentObject obj in Grasshopper.Instances.ActiveCanvas.Document.Objects)
    {
        //Looping through the objects in the active canvas document
        if( (obj.ComponentGuid == this.ComponentGuid) && (obj.InstanceGuid != this.InstanceGuid) )
        {
            //If the component Guid matches and if the instance Guids are different...
            System.Windows.Forms.MessageBox.Show("There is already an instance of this component on the canvas. Please delete it before adding a new one.", "Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); //Tell the user what's going on...
            Grasshopper.Instances.ActiveCanvas.Document.RemoveObject(this, false); //True or false - do we want to recompute the canvas? We're removing a component that was unnecessary, so I'm setting this to false - we don't need to recompute the canvas
            break; //We found and removed our additional instance, we don't need to continue looping (saves computational time and prevents a Grasshopper complaint that the collection (Document.Objects) was modified when you're trying to use it!
        }
    }

    base.AddedToDocument(document);
}

Download test file

Want to see this in operation without messing with your current work, or without creating a new component? Download the file linked below and build it into a Grasshopper component and you’ll have a component which adds two numbers together, but won’t let you have more than one instance of it on the canvas at a time.

Download the test file here.

Leave a Reply

Your email address will not be published. Required fields are marked *