Draw Full Names Creates New Component Instances?

I’m wondering if anyone has experienced this problem before.

I have created custom Grasshopper components that have private instances of other classes stored on them. This is super useful because it allows complex information (such as lists, dictionaries, classes, etc) to be saved and loaded with the GH file by writing/reading the data as JSON via chunks. Storing this data on the component is absolutely necessary for what I’m doing.

I just realized, through some debugging, that when you click Draw Full Names, it runs through the whole canvas and creates new instances of each component, this time drawing them with the full names. By doing this, all of the data that was stored on the initial component is lost. The sweep of the canvas does not transfer any of the private fields over. And this appears irrecoverable. Unchecking Draw Full Names appears to do the same thing.

Now, I understand from reading a few other posts that the implementation of Draw Full Names is a huge hack. But it also breaks these types of custom components.

Question #1: How does Draw Full Names create a new instance of each component? Is there some sort of Copy function on a component that can be overridden that returns a new instance? Or does it simply find the type and call the constructor? If it is as simple as overriding a Copy function, that would be fantastic.

Question #2: If I can’t override a component-specific Copy function, can I override the method that is called when Draw Full Names is handled? At a canvas level? I don’t want to override how a component itself is drawn. I want to handle this event in such a way that, when Draw Full Names is clicked, I can catch it before it loops through all of the components and creates new instances. This way, I can ensure that, at least for my components, the private fields are appropriately transferred. I feel like the answer to this would be no, as it doesn’t seem like it could be plug-in specific.

Question #3: Is there a reason why new instances of the components are created in the first place? Like I said, I understand the implementation was hacky, but creating new instances seems odd. I would never have realized this had I not put a breakpoint in my constructor and watched it get it after clicking Draw Full Names.

Thanks for any info!

It looks like when toggling Draw Full Names on and off that the components retain their InstanceGuid. It may be that the hacky implementation of Draw Full Names would require a hacky fix.

It seems a bit annyoing and overwrought for such an edge case, but one thing you might be able to do is temporarily store your JSON in a static dictionary field in the component class whenever a component is removed from the document (override RemovedFromDocument) using the Instance Guid as your key, and then checking this dictionary when a component is AddedToDocument. If the key exists, then update the newly added component’s fields with the old values. Certainly not ideal, but perhaps workable. You could clear the dictionary at any number of times after that, even just in SolveInstance.

@dave_stasiuk That was a great suggestion and I thought it would be the answer! However, I just tried implementing it but the RemovedFromDocument and AddedToDocument functions are not called when Draw Full Names is checked on/off. Very odd indeed.

Interesting and surprising…maybe try subscribing to ObjectChanged or AttributesChanged events?

Just subscribed to both events and neither one appears to be triggered by Draw Full Names. Subscribed to the events in the constructor and stepped through to make sure the subscription code was hit. Put breakpoints in both functions that were added to handle the events, and neither one got hit. This keeps getting more and more interesting!

Odd. One more option could be to simply maintain your fields in a static dictionary, and just access them directly from there, again using the InstanceGuid as the key. You’d still need to manage populating/storing it with values on load/save.

Yah, I believe that would work, but I would call that the nuclear option! Might be the only option, though. I’m going to play around with it a little more and see if I can figure out how these components are created and added without triggering any of the events.

Does the GH_Document.SettingsChanged event fire?

Sorry, had to get back to other work! Unfortunately GH_Document.SettingsChanged does not fire. Other settings, such as Preview mode (i.e. wireframe preview) do trigger the event.

So just for a little more info (that frankly doesn’t help much), the call stack is:

So it calls the document’s ConvertNickNamesToFullNames() method, which then appears to create a new instance using the GH_CompiledObjectProxy. That last class does not appear to be public.

To override the ConvertNickNamesToFullNames() method, I’d have to create a new document class that inherits from GH_Document, and I don’t think that can really be done, as everything upstream would have to change.


So, it turns out that there is an event in Grasshopper.CentralSettings called CanvasFullNamesChanged. I can subscribe to that. This event fires before new instances of the objects are created, so it gives me a chance to save the data. However, the very bad news is that the instance GUIDs are not the same!!! Are you kidding me!? Haha. Grasshopper has decided that it should not be this easy. So there is no way for me to relate the dictionary values to the new instances, assuming that you could have multiple of the same component on the canvas.


I’m an idiot. I mean…I can be sometimes, and this was one of those times. So, I learned a lot floundering around here, but it turns out that I was doing something really stupid in the constructor that was resetting some global variables that in turn made it look like info was lost. Yes, Draw Full Names goes through and creates new instances of all components on the canvas. But the reality is that all of the data appears to be retained and I just cried wolf a lot. But again, this was interesting. I learned quite a bit about how Draw Full Names works after digging into the source code a bit.

1 Like