I have a question about event handling, in particular, how to best handle multiple ‘simultaneous’ events when they are triggered and what strategies are recommended.
An example might be this: A user performs a ‘join’ on a collection of selected rhino geometry, say 20 objects, which results in the event sequence of an addition of one new object and then the deletion of the 20 previous objects. My questions has to do with handling these events. For the purpose of my application I don’t need to know that 20 deletion events have occurred.
Finally: The application being written is for Grasshopper. The events are being used to trigger new grasshopper solutions and performance issues are emerging in examples such as the one given above.
I understand that simultaneous events do not take place in Rhino. Please excuse my using punctuation ’ … ’ around the word simultaneous to try and express the UX of having one command trigger several sequential events at ‘once’.
I also understand that I can ignore events or not capture or watch them, that’s clear. What isn’t so clear is the interaction between grasshopper and rhino when an event is needed as a part of triggering a new grasshopper solution but when only one of them, or a reduced number of them are required - as in the case of having a use join a large amount of geometry in the rhino environment.
I hope this is clearer - please excuse any lack of clarity in the previous post.
What might be most useful in terms of coordinating between rhino events and the Grasshopper solution space would be to have any event that may involve multiples structured in a similar way to those such as EndUndo or EndCommand.
For example EndAddObject or End DeleteObject. This would then enable solutions to be triggered at the right moment, after Rhino had done it’s related work.
I’m not sure I have any “strategies” that I can recommended. But I’ll ramble on anyway - maybe I’ll get lucky.
In general, Grasshopper doesn’t modify the Rhino document. It dynamically draws pretty much everything (until you bake). Thus, Grasshopper shouldn’t trigger too many events unless you are writing custom code which modifies the document
Event handling functions should be very, very fast, keeping in mind that you are not the only one listening to events.
You should never modify anything in the document in an event handler.
You do want to do too much in an event handler other than record the fact that an event was triggered. When Rhino is idle - RhinoApp.Idle - then you can process the events that you recorded and take whatever action you want.
If there are commands that drive your plug-in crazy, then watch for the command to begin. Then, when the command ends, follow up and take whatever action you need.
Object replacement can be a little tricky due to Rhino’s undo capability. If both RhinoDoc.UndoActive and RhinoDoc::RedoActive are false, then immediately after RhinoDoc.ReplaceRhinoObject is called there will be a call to RhinoDoc.AddRhinoObject and then a call to RhinoDoc.DeleteRhinoObject, as Rhino replaces the original and pushes the old to the undo stack.
If either RhinoDoc.UndoActive or RhinoDoc::RedoActive are true, then immediately after RhinoDoc.ReplaceRhinoObject is called there will be a call to RhinoDoc.DeleteRhinoObject and then a call to RhinoDoc.UndeleteRhinoObject, as an undo or redo operation is in effect.
A strategy that we employ to collect multiple events is to use a timer:
private DispatcherTimer _timer;
private const int _eventCollectionTimespan = 10; // 10 ms to collect multiple events
private void OnEvent(object o, EventArgs a)
// each event come here, but only the first creates the timer
if (null == _timer || !_timer.IsEnabled)
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(eventCollectionTimespan);
_timer.Tick += OnTimerTick;
// after 10 ms this gets called once, after multiple events have been handled
private void OnTimerTick(object o, EventArgs a)
if (null != _timer)
_timer = null;
Dear Dale - i could not find RhinoDoc.UndoActive nor RhinoDoc.RedoActive in RhinoCommon.
As a workaround i use:
Rhino.Commands.Command.BeginCommand += OnBeginCommand; Rhino.Commands.Command.EndCommand += OnEndCommand;
and check CommandEventArgs CommandEnglishName == “Redo” or “Undo” and then set a boolean flag - to replace above Properties.
Works fines so fare - or is there any argument against this approach ?