A "Component Moved on Canvas" event in Grasshopper

Hi there! I am looking for an event that fires when a pivot of a custom Grasshopper component changes its location on the canvas. I’ve tried the following events:

  • AttributesChanged - doesn’t fire
  • DisplayExpired - doesn’t fire
  • ObjectChanged - doesn’t fire
  • PreviewExpired - didn’t expect it to fire to begin with

I’ve also tried approaching this from the opposite direction by tapping into some events on the document and on the canvas:

  • doc.ContextChanged - doesn’t fire
  • doc.ModifiedChanged - not sure if relevant, but doesn’t fire
  • canvas.DragDrop - likely not relevant and doesn’t fire
  • canvas.Invalidated - fires way too often
  • canvas.Click - fires way too often
  • canvas.Move - doesn’t fire
  • canvas.LocationChanged - probably not relevant and doesn’t fire

I even tried inheriting from INotifyPropertyChanged, but obviously, the setter on the Pivot property doesn’t raise the PropertyChanged event, so this was a largely futile pursuit as well. Any thoughts?

1 Like

Create custom component attributes, override Pivot and raise an event in its setter.

1 Like

For anyone else who will find this helpful. Based on the combined wisdom from @andheum and @Dani_Abalde, this is what I ended up with (works perfectly):

Here is my custom ComponentAttributes class:

public class CustomComponentAttributes : GH_ComponentAttributes
{
    // Just to keep track of the old location (optional)
    public PointF LastKnownPivot { get; set; }

    public CustomComponentAttributes (IGH_Component comp) : base(comp)
    {
        this.LastKnownPivot = new PointF(this.Pivot.X, this.Pivot.Y);
    }

    public override PointF Pivot 
    {
        get => base.Pivot;
        set
        {
            base.Pivot = value;
            // Raise custom event
            OnPivotMoved(new ComponentMovedEventArgs(
                this.Owner.InstanceGuid,
                this.Owner.ComponentGuid,
                this.LastKnownPivot,
                this.Pivot));
        }
    }

    // The custom event being raised from the Pivot setter
    public event EventHandler PivotMoved;
    public void OnPivotMoved(ComponentMovedEventArgs e)
    {
        EventHandler handler = PivotMoved;
        handler?.Invoke(this, e);
        this.LastKnownPivot = new PointF(this.Pivot.X, this.Pivot.Y);
    }
}

Here are my custom event arguments (they can be whatever you want them to be):

public class ComponentMovedEventArgs : EventArgs
{
    public Guid InstanceGuid { get; set; }
    public Guid ComponentGuid { get; set; }
    public PointF OldPivot { get; set; }
    public PointF NewPivot { get; set; }

    public ComponentMovedEventArgs(
        Guid instanceGuid, 
        Guid componentGuid, 
        PointF oldPivot,
        PointF newPivot)
    {
        this.InstanceGuid = instanceGuid;
        this.ComponentGuid = componentGuid;
        this.NewPivot = newPivot;
    }
}

And this is my custom component:

public class MyCustomComponent : GH_Component
{
    // Set custom attributes
    public override void CreateAttributes()
    {
        Attributes = new CustomComponentAttributes(this);
        ((CustomComponentAttributes )Attributes).PivotMoved += CustomComponent_PivotMoved;
    }

    private void CustomComponent_PivotMoved(object sender, EventArgs e)
    {
        // Re-transmitting the event from inside the component itself
        ComponentMovedEventArgs args = e as ComponentMovedEventArgs;
        this.OnComponentMoved(args);
    }

    // Event exposed on the component itself
    public event EventHandler ComponentMoved;
    public void OnComponentMoved(ComponentMovedEventArgs e)
    {
        EventHandler handler = ComponentMoved;
        handler?.Invoke(this, e);
    }
}

So now I can just do this:

MyCustomComponentInstance.ComponentMoved += <SomeEventHandlerHere>

Thank you so much for the help!

2 Likes