Event handling - doc.Objects.Replaced on multiple Objects

I use doc.Objects.Replaced event to update attributes (weight, length) in the Object’s User Text and fix the geometry if the user change has resulted in a geometry invalid for my use.
I accomplish this by listening to the OnObjectsReplaced event and then making document changes on the next RhinoIdle.
This is where my issue comes in, as when the user only changes a single object (e.g. control point drag) it executes fine. But when mutliple control points of different objects are dragged, the event does in my case not fire for every object that has changed, but only fires for the first one, and the Agrs only contain a single Id.
This is my current OnObjectsReplaced event handler:

        private void OnObjectReplaced(object sender, Rhino.DocObjects.RhinoReplaceObjectEventArgs e)
        {
            if (!RhinoDoc.ActiveDoc.UndoActive)
            {
                UpdateEventSubscriptions(false);

                var doc = RhinoDoc.ActiveDoc;
                if (doc == null)
                    return;

                Guid replacedId = e.ObjectId;
                if (!_oldGeometryById.ContainsKey(replacedId))
                {
                    var oldObj = doc.Objects.Find(replacedId);
                    if (oldObj != null && oldObj.Geometry != null)
                    {
                        GeometryBase oldCopy = oldObj.Geometry.Duplicate();
                        _oldGeometryById[replacedId] = oldCopy;
                    }
                }

                _pendingReplacedGeoms.Add(replacedId);

                RhinoApp.Idle += OnIdleRecalculate;
            }

Does the Replaced event actually not fire per object?

We probably need to see more of your setup to be able to help more, but off the top of my head RhinoApp.Idle += OnIdleRecalculate looks like it is being subscribed too every time OnObjectReplaced is fired, which could cause you grief. You could do something like this instead:

//track whether RhinoApp.Idle has been subscribed to
private bool _isSubscribed;

//add check in OnObjectReplaced to see if idle event is already setup
if(!_isSubscribed)
{
    RhinoApp.Idle += OnIdleRecalculate;
    _isSubscribed = true;
}

//at the end of OnIdleRecalculate remove event hook and reset _isSubscribed
private void OnIdleRecalculate()
{
    //your processing logic

    RhinoApp.Idle -= OnIdleRecalculate;
    _isSubscribed = false;
}

You can also make use of the RhinoReplaceObjectEventArgs – it has properties for NewRhinoObject and OldRhinoObject, as well as Document (which you can using instead of RhinoDoc.ActiveDoc): https://developer.rhino3d.com/api/rhinocommon/rhino.docobjects.rhinoreplaceobjecteventargs

If you share what UpdateEventSubscriptions and OnIdleRecalculate are doing then it’ll probably be more obvious what’s gone wrong.

– Luke

1 Like

Thank you Luke, I myself was ofc responsible for this headache, I now use your boolean to not subscribe every time and took out the call to UpdateEventSubscriptions, which I use on many occasions to hook/unhook all handlers, to isolate the behaviour of a Command or Handler. For this Handler I now only unsubscribe from Delete, because it would otherwise fire in the Handler, and resubscribe after execution of OnIdleRecalculate.
This is my first time working with Rhinocommon Events, therefore I wonder if there is maybe a pattern which I should follow to avoid my Plugin getting too crowded with individual and therefore confusing singular event unhooks/reehooks?
Sometimes I think that what Im attempting here is generally not intended to be done and therefore so sketchy.

1 Like

It really depends on what you’re trying to achieve and how you’re doing it – the above approach can be good if you’re working within a command, but if you’re trying to do more “global” event watching then it might not be ideal.

If you haven’t already, this RhinoCommon Event example is good to play around with: https://github.com/mcneel/rhino-developer-samples/blob/7a8e6f05428736d152745f2fe1364f273587b93f/rhinocommon/cs/SampleCsEventWatcher/SampleCsEventWatcherPlugIn.csIs – particular to understand how events work. I can’t remember exactly but I’m pretty sure ReplaceObject event will fire a Delete and Add event as well, since the old object is deleted and the new object is added.

I’m definitely not an expert but if you have more examples to show I can take a look.

Hi @Georg,

Attached is a sample of a pattern we use frequently.

TestGeorgCommand.cs (4.5 KB)

– Dale

3 Likes