C# - Toggle between a Component Classic Attributes and Custom Attributes?

Hi all,

I am trying to create a component that can toggle between a Custom defined Component Attributes that I have created and the Classic Component Attributes through the Right click menu.

  • The Custom Component Attribute is working as expected
  • Custom Append Additional Component Menu Items also working
  • All Write and Read Logic set in place and working

I am trying to toggle between the radio bottoms UI and the classic parameter access for a Boolean input but I can’t figure out how to change between my custom component attributes and the classical component attribute.

I found this old post but could not get my head around the discussion.

Any suggestions on how I could achieve my goal? it would be greatly appreciated any recommendation!

Thanks in advance!

Bets regards,

Iker

I was able to figure out how to switch between Component Default Attributes and the Custom Component Attributes.

I only needed to use the menu toggle bool to flip between the the to logics inside the attributes overrides
Default Component Attributes - (base.Layout / base.render / base.MouseEvent / base.ExpireLayout) and the Custom Component Attributes logic!

Video_2022-03-14_130453

But now my issue is that I can’t register the optional input Boolean parameters when I toggle to the Component Default attributes.

I really don’t know whether this is something that requires to be handled in the GH_Component or directly in the GH_ComponentAtrributes Class.

I tried handle this directly in the GH_Component with the menu toggle but does not work and throws an message error when I run the component.

The other thing I can think off is to directly override the custom attributes for the input params not sure how to do it or it may affect the aready existing default Attribute tree :s

Any suggestions?

RegisterInput/OutputParams is called only once when the component is created. You should manipulate GH_Component.Params.Input directly.

Thanks Keyu!, any reference I could look for?

currently looking into the code of this old post but seems a bit of head ache! :S

An implementation not using variable-parameter component:

using Grasshopper.Kernel;
using Grasshopper.Kernel.Attributes;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace PancakeAlgo.Misc
{
    public class TestOptionableComponent : GH_Component
    {
        public TestOptionableComponent()
          : base("TestOptionableComponent", "TestOptionableComponent",
              "Description",
              "Category", "Subcategory")
        {
        }
        protected override System.Drawing.Bitmap Icon
            => null;

        public override Guid ComponentGuid
            => new Guid("d5c0a955-3593-4c49-9264-40b35cfcaec9");

        private const string ConfigOptionAsInput = "OptionAsInput";
        private const bool DefaultOptionAsInput = false;
        private bool OptionAsInput
        {
            get => GetValue(ConfigOptionAsInput, DefaultOptionAsInput);
            set => SetValue(ConfigOptionAsInput, value);
        }

        private bool _hasOptionInput = false;
        private int _unchangedParamCount;

        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
            pManager.AddNumberParameter("Unchanged", "U", "", GH_ParamAccess.item);

            _unchangedParamCount = pManager.ParamCount;
        }

        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
            pManager.AddBooleanParameter("WhichStyle?", "WS?", "", GH_ParamAccess.item);
        }

        public override void AddedToDocument(GH_Document document)
        {
            RebuildInputParameter();

            base.AddedToDocument(document);
        }

        protected override void SolveInstance(IGH_DataAccess DA)
        {
            DA.SetData(0, OptionAsInput);
        }

        public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
        {
            Menu_AppendItem(menu, "Input style", MenuClick_SwapInputStyle, true, OptionAsInput);

            base.AppendAdditionalMenuItems(menu);
        }

        private void MenuClick_SwapInputStyle(object sender, EventArgs e)
        {
            OptionAsInput = !OptionAsInput;
            RebuildInputParameter();
        }

        private void RebuildInputParameter()
        {
            if (OptionAsInput && !_hasOptionInput)
            {
                foreach (var param in GenerateChangableInputParams())
                {
                    Params.RegisterInputParam(param);
                }

                _hasOptionInput = true;
                Params.OnParametersChanged();
                ExpireSolution(true);
            }
            else if (!OptionAsInput && _hasOptionInput)
            {
                for (var paramIndex = Params.Input.Count - 1;
                    paramIndex >= _unchangedParamCount; --paramIndex)
                {
                    Params.UnregisterInputParameter(Params.Input[paramIndex]);
                }

                _hasOptionInput = false;
                Params.OnParametersChanged();
                ExpireSolution(true);
            }
        }

        private static IEnumerable<IGH_Param> GenerateChangableInputParams()
        {
            yield return new Param_Boolean
            {
                Name = "Switch",
                NickName = "S",
                Description = "",
                Access = GH_ParamAccess.item
            }.WithDefaultValue(false);
            yield return new Param_Integer
            {
                Name = "OtherInfo",
                NickName = "OI",
                Description = "",
                Access = GH_ParamAccess.item
            }.WithDefaultValue(0);
        }
    }

    internal static class ParamExtensions
    {
        public static GH_PersistentParam<T> WithDefaultValue<T>(
            this GH_PersistentParam<T> param,
            params object[] values)
            where T : class, IGH_Goo
        {
            param.SetPersistentData(values);
            return param;
        }
    }
}
4 Likes

Thank you Keyu!, I will go over your implementation, from your video it seems it is exactly what I needed!

much appreciated,

Best Iker

Hi @gankeyu (Keyu Gan),

Thanks to your example I was able to brake down the process for the toggle style option (Thanks again!).
I had to code it differently to make it compatible with my write a read implementation, but overall logic is similar.

Now I am struggling with the RecordUndoEvent(“String”).
e.i: Custom Component Options with C#, VB

The issue seems to be that when I try to call for RecordUndoEventy(“String”) the component crash.

I have literally no experience nor idea on how to setup undo events , allowing reconnecting wires back to the Boolean toggles and also reverting back to the component previous style state while registering back the parameters.

have you got any thoughts or any way on how to approach this?

initial state:

if a change to collapse and then try to Ctrl+z

I try to follow this old post but is beyond me! :s

apologize i’ve been busy recently. Would investigate it a little bit afterwards.

Hi @gankeyu (Keyu Gan)

All resolved!, standard GH_Component do not supports (De)Serialization for “RecordUndoEvent” for Parameters created outside of the provide standard means (RegisterInputParams), that is why there were issue when undoing /redoing events.

The only way, to properly (De)Serialization the component parameters when undoing or redoing (Ctrl+Z or Ctrl+Y), is by implementing the IGH_VariableParameterComponent interface.

As soon as you implement the interface (empty - with minimum for the methods to run), immediately starts to handling the “RecordUndoEvent” (De)Serialization for the custom Parameters like charm!

Old post from David: Adding input/output parameters without using IGH_VariableParameterComponent - Grasshopper

Now it handles all the undos in RecordUndoEvent(“UndoEvent”) - Option style + reconecting wires + etc…

Thanks for you help hope this helps other too!

1 Like

yes, that would force Grasshopper to (de)serialize parameters on undo actions.

1 Like