How to create dropdown component in GH C#?

How to create a dropdown in C#?
I have a list on enums:

public enum PanelType
{
    Undefined,
    Wall,
    WallExternal,
    WallInternal,
}

and trying to make a component that will allow user to select via the drop down… here is my GH script for the component below. Any help will be really appreciated :slight_smile: :stuck_out_tongue:

    namespace SAM.Analytical.Grasshopper
    {
        public class AnalyticalPanelType : GH_Component
        {
            /// <summary>
            /// Panel Type
            /// </summary>
            public AnalyticalPanelType()
              : base("AnalyticalPanelType", "AnalyticalPanelType",
                  "Snap Panels",
                  "SAM", "Analytical")
            {

            }

            public override bool AppendMenuItems(ToolStripDropDown menu)
            {
                base.AppendMenuItems(menu);

                ToolStripDropDown toolStripDropDown = new ToolStripDropDown();

                foreach (PanelType panelType in Enum.GetValues(typeof(PanelType)))
                    toolStripDropDown.Items.Add(panelType.ToString());

                Control control = new Control();
                global::Grasshopper.Kernel.GH_DocumentObject.Menu_AppendCustomItem(toolStripDropDown, control);

                return true;
            }

            /// <summary>
            /// Registers all the input parameters for this component.
            /// </summary>
            protected override void RegisterInputParams(GH_InputParamManager inputParamManager)
            {

            }

            /// <summary>
            /// Registers all the output parameters for this component.
            /// </summary>
            protected override void RegisterOutputParams(GH_OutputParamManager outputParamManager)
            {
                outputParamManager.AddGenericParameter("PanelType", "PanelType", "Analytical PanelType", GH_ParamAccess.item);
            }

            /// <summary>
            /// This is the method that actually does the work.
            /// </summary>
            /// <param name="dataAccess">The DA object is used to retrieve from inputs and store in outputs.</param>
            protected override void SolveInstance(IGH_DataAccess dataAccess)
            {

            }

            /// <summary>
            /// Provides an Icon for the component.
            /// </summary>
            protected override System.Drawing.Bitmap Icon
            {
                get
                {
                    //You can add image files to your project resources and access them like this:
                    // return Resources.IconForThisComponent;
                    return Resources.HL_Logo24;
                }
            }

            /// <summary>
            /// Gets the unique ID for this component. Do not change this ID after release.
            /// </summary>
            public override Guid ComponentGuid
            {
                get { return new Guid("25a6b405-19ab-4ff1-9666-7760997ccfdd"); }
            }
        }
    }

(not tested)

        public override bool AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
        { 
            foreach (PanelType panelType in Enum.GetValues(typeof(PanelType)))
                GH_Component.Menu_AppendItem(menu, panelType.ToString(), Menu_PanelTypeChanged).Tag = panelType;
            return true;
        }

        private void Menu_PanelTypeChanged(object sender, EventArgs e)
        {
            if(sender is ToolStripMenuItem item && item.Tag is PanelType panelType)
            {
                //Do something with panelType
            }
        }

thanks a lot for help

I used a few solutions from:

It almost works I just missing tick boxes when I tick selection :relaxed:
I will post the whole code so it will be a good example for other new scripters…

    namespace SAM.Analytical.Grasshopper
    {
        public class AnalyticalPanelType : GH_Component
        {
            private PanelType panelType = PanelType.Undefined;
            
            /// <summary>
            /// Panel Type
            /// </summary>
            public AnalyticalPanelType()
              : base("AnalyticalPanelType", "AnalyticalPanelType",
                  "Snap Panels",
                  "SAM", "Analytical")
            {

            }

            public override bool Write(GH_IWriter writer)
            {
                writer.SetInt32("PanelType", (int)panelType);
                return base.Write(writer);
            }

            public override bool Read(GH_IReader reader)
            {
                int aIndex = -1;
                if (reader.TryGetInt32("PanelType", ref aIndex))
                    panelType = (PanelType)aIndex;
                
                return base.Read(reader);
            }

            protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
            {
                foreach (PanelType panelType in Enum.GetValues(typeof(PanelType)))
                    GH_Component.Menu_AppendItem(menu, panelType.ToString(), Menu_PanelTypeChanged).Tag = panelType;

                base.AppendAdditionalComponentMenuItems(menu);
            }

            private void Menu_PanelTypeChanged(object sender, EventArgs e)
            {
                if (sender is ToolStripMenuItem item && item.Tag is PanelType)
                {
                    //Do something with panelType
                    this.panelType = (PanelType)item.Tag;
                    ExpireSolution(true);
                }
            }

            /// <summary>
            /// Registers all the input parameters for this component.
            /// </summary>
            protected override void RegisterInputParams(GH_InputParamManager inputParamManager)
            {

            }

            /// <summary>
            /// Registers all the output parameters for this component.
            /// </summary>
            protected override void RegisterOutputParams(GH_OutputParamManager outputParamManager)
            {
                outputParamManager.AddGenericParameter("PanelType", "PanelType", "Analytical PanelType", GH_ParamAccess.item);
            }

            /// <summary>
            /// This is the method that actually does the work.
            /// </summary>
            /// <param name="dataAccess">The DA object is used to retrieve from inputs and store in outputs.</param>
            protected override void SolveInstance(IGH_DataAccess dataAccess)
            {
                dataAccess.SetData(0, panelType);
            }

            /// <summary>
            /// Provides an Icon for the component.
            /// </summary>
            protected override System.Drawing.Bitmap Icon
            {
                get
                {
                    //You can add image files to your project resources and access them like this:
                    // return Resources.IconForThisComponent;
                    return Resources.HL_Logo24;
                }
            }

            /// <summary>
            /// Gets the unique ID for this component. Do not change this ID after release.
            /// </summary>
            public override Guid ComponentGuid
            {
                get { return new Guid("25a6b405-19ab-4ff1-9666-7760997ccfdd"); }
            }
        }
    }
        protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
        {
            foreach (PanelType pt in Enum.GetValues(typeof(PanelType)))
                GH_Component.Menu_AppendItem(menu, pt.ToString(), Menu_PanelTypeChanged, true, pt == this.panelType).Tag = pt;

        }

where this.panelType is a variable in the component context.

1 Like

thanks a lot

@michaldengusiak this post is really useful! When I am defining the panelType variable I got the error that PanelType does not contain Undefined. Do you think it is a problem with the references? Any advice?

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Rhino.Geometry;
using Rhino.UI;

namespace ExtendingGUIList
{
    public class DropDownExample : GH_Component
    {
        private PanelType panelType = PanelType.Undefined;
        /// <summary>
        /// Initializes a new instance of the DropDownExample class.
        /// </summary>
        public DropDownExample()
          : base("DropDownExample", "Nickname",
              "Description",
              "AIOS", "Subcategory")
        {
        }

        public override bool Write(GH_IWriter writer)
        {
            writer.SetInt32("PanelType", (int)panelType);
            return base.Write(writer);
        }

        public override bool Read(GH_IReader reader)
        {
            int aIndex = -1;
            if (reader.TryGetInt32("PanelType", ref aIndex))
                panelType = (PanelType)aIndex;

            return base.Read(reader);
        }

        protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
        {
            foreach (PanelType panelType in Enum.GetValues(typeof(PanelType)))
                GH_Component.Menu_AppendItem(menu, panelType.ToString(), Menu_PanelTypeChanged).Tag = panelType;

            base.AppendAdditionalComponentMenuItems(menu);
        }

        private void Menu_PanelTypeChanged(object sender, EventArgs e)
        {
            if (sender is ToolStripMenuItem item && item.Tag is PanelType)
            {
                //Do something with panelType
                this.panelType = (PanelType)item.Tag;
                ExpireSolution(true);
            }
        }

        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
            pManager.AddGenericParameter("PanelType", "PanelType", "PanelType is the best", GH_ParamAccess.item);
        }

        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
        }

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon
        {
            get
            {
                //You can add image files to your project resources and access them like this:
                // return Resources.IconForThisComponent;
                return null;
            }
        }

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid
        {
            get { return new Guid("7C82C338-CB29-4544-B3BC-5463DDB0CE65"); }
        }
    }
}

hello, I had the same issue; replying late should it help others he’s the solution:

You’ve forgotten to add the enumerated list object in Michal’s original post, add this code:

public enum PanelType
{
    Undefined,
    Wall,
    WallExternal,
    WallInternal,
}

Note, PanelType is the name of the Rhino.UI object type and is a keyword from the API.
panelType is the chosen name for the object created from the new constructor by the author. (hint: edit this name)