How to check has a input or not in custom component?

for example:

i found in this code,it cannot reach the “else” part, when there is no input for the component at all…
Can anyone why how to fix this problem(pls neglect the small pink rectangle,just for custom attributs test )?
Thank you!

    var ix = Component.Params.IndexOfName("v");
    var has_input = Component.Params.Input[ix].SourceCount > 0;

Or if you keep track of the (input) Params index yourself:

    var has_input = Component.Params.Input[0].SourceCount > 0;
    if (has_input) 
        Component.Message = "has input" ;
        Component.Message = "no input" ;

Be aware if Output params because no event is fired when changing them, so your code wouldn’t be updated when removing a wire. On the Inputs it works though. (See the problem with updating Outputs in this post from yesterday)

// Rolf

Hello RIL
thank you
i have tried your method,but if i disconnect the connection,the Message is also unchange!is 3 also.

i guess may be some refresh method is missing…

To me it doesn’t look like your input name is “input” (which you use in your code). The name appears to be “v”. But in order to avoid any name trouble, use zero (0) as index for the first input. It should work (I just tried it on my own definition. It updates as expected.

Edit: Try this file. It updates as expected. (7.2 KB)

// Rolf

RIL,thankyou,i open the gh file,it works nice,but i want to do in custom component,in visual seems also no works…(i replace the "input " into 0)

Perhaps you must subscribe to the Input event in the VS version? Try this:

    // In SolveInstance, add this:
    this.Params.ParameterChanged -= this.OnParamsChanged;
      // Do your thing
      // ...
      this.Params.ParameterChanged += this.OnParamsChanged;

  // Your own eventhandler responding to changes on the input:
  public void OnParamsChanged(object source, EventArgs e)
    RhinoApp.WriteLine("Event was fired! (An Input or Output was connected or disconnected)");
    var ix = this.Params.IndexOfInputParam("v");
    var cnt = this.Params.Input[ix].SourceCount;
    var has_input = cnt > 0;
    if (has_input)
      this.Message = "has input " + cnt.ToString();
      this.Message = "no input";

I tried your method,this method is more difficult,it works almost good,but only one thing is no good:
when I drag the component from toolbar into canvas,the component Message doesnot show"no input"…just after i connect a input (message show “has input”)and disconnect the input,the message show “no input”…almost good,just how to fix when first drag into canvas to show “no input”,thank you RIL

ComponentTest.cs (4.8 KB)
this is code…
and another question …this method need a lot of work…has another easier?thankyou…

Yes, this is because a component being placed on the Canvas has not yet had the chance to subscribe to anything… :slight_smile:

Unfortunately I have not (yet) tried all the events if there’s any other way one could activate this check, but perhaps @DavidRutten has any hints on how to catch a related event.

There are lots of events being fired, if you just can catch them when needed… :slight_smile:

// Rolf

So reading this top to bottom and replying in that order. The reason that your else never runs is because the input isn’t optional. When a non-optional input doesn’t have data the SolveInstance() method isn’t even called. You can mark inputs optional when you add them inside RegisterInputParams()

The named accessor uses the name, not the nickname of the parameter. The nickname can be easily changed by the user so it’s not considered a good identifier. However @RIL is right in that the index accessor is much more reliable and should be used whenever possible.

Thank you,David,the method works.

thank you RIL,i learn a lot from you code,thank you.

David,it doesnot work in this case:

because the "XYZEAxis…"is cannot add optional argument…(see picture),because of custom class. @DavidRutten

More on index accessors. It took me long before I discovered that the pManager actually returns the index of newly registered Params. Using those index values has two major benefits:

  1. Avoiding trouble when renaming Params.
  2. Avoiding trouble when the reordering Params (if hardcoding the index numbers)

So for these reasons I started using variables “IN_Params” and “OUT_Params” when registering them, see code below (see also how the IN_Name and OUT_Name indexes are used in the SolveInstance code far below)

private int IN_Mesh;
private int IN_Radius;
private int IN_CenterPoints;
private int IN_Directions;
private int IN_Iterations;
private int IN_Optional;

private int OUT_HitPoints;
private int OUT_CircumPoints;
private int OUT_IntersectRays;
private int OUT_MeshFaces;
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
    IN_Mesh = pManager.AddMeshParameter("Mesh", "M", "Blah, blah, blah", GH_ParamAccess.item);
    IN_Radius = pManager.AddNumberParameter("Radius", "R", "", GH_ParamAccess.list);
    IN_CenterPoints = pManager.AddPointParameter("CenterPoint", "C", "Blah, blah, blah", GH_ParamAccess.list);
    IN_Directions = pManager.AddVectorParameter("Directions", "D", "Blah, blah, blah", GH_ParamAccess.list);
    IN_Iterations = pManager.AddIntegerParameter("Iterations", "I", "Blah, blah, blah", GH_ParamAccess.item, 10);
    IN_Optional = pManager.AddBooleanParameter("Optional output", "O", "Blah, blah, blah", GH_ParamAccess.item, false);
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
    OUT_HitPoints = pManager.AddPointParameter("ContourPoints", "H", "Blah, blah, blah", GH_ParamAccess.tree);
    OUT_CircumPoints = pManager.AddPointParameter("RayPoints", "C", "Blah, blah, blah", GH_ParamAccess.list);
    OUT_IntersectRays = pManager.AddVectorParameter("IntersectRays", "R", "Blah, blah, blah", GH_ParamAccess.list);
    OUT_MeshFaces = pManager.AddMeshFaceParameter("MeshFace", "F", "Blah, blah, blah", GH_ParamAccess.list);
protected override void SolveInstance(IGH_DataAccess DA)
    // ===================
    // ===================
    Mesh m_mesh = null;
    if (!DA.GetData(IN_Mesh, ref m_mesh))
        throw new System.ArgumentNullException("(Mesh)");
    var m_radius = new List<double>();
    if (!DA.GetDataList(IN_Radius, m_radius))
        throw new System.ArgumentNullException("Invalid Radius List.");
    var m_centerpts = new List<Point3d>();
    if (!DA.GetDataList(IN_CenterPoints, m_centerpts))
        throw new System.ArgumentNullException("Invalid Center Point List.");
    var m_directions = new List<Vector3d>();
    if (!DA.GetDataList(IN_Directions, m_directions))
        throw new System.ArgumentNullException("Invalid Directions List.");
    var m_iterations = -1;
    if (!DA.GetData(IN_Iterations, ref m_iterations))
        throw new System.ArgumentNullException("Invalid Iterations value");
    var m_optional = false;
    if (!DA.GetData(IN_Optional, ref m_optional))
        // throw new System.ArgumentNullException("Invalid Optional List.");

In this way the code becomes crystal clear and less error prone. No more bugs related to renaming or reordering inputs and outputs. :slight_smile:

// Rolf

good idea!

Optionality is a property of IGH_Param, so you can assign that property either before or after:

  int index = pManager.AddParameter(new XYZEAxisParam(), ...);
  pManager[index].Optional = true;


IGH_Param param = new XYZEAxisParam();
param.Optional = true;
pManager.AddParameter(param, ...);

pManager.AddParameter(new XYZEAxisParam() { Optional = true }, ...);

David ,i found this way also can:

add a default value ,also can do what i want.

but in custom paramter,the pmanger doesnot support add a default value…

more concise

default values and optional parameters are not the same thing. They may both result in having your SolveInstance method called, but they do so in different ways. It is not unthinkable that you may want both a default value and an optional parameter.

If you have your own parameter type, then you must assign default values to the PersistentData yourself.