tips & tricks for zerotouch libraries in dynamo

image by archi-lab
  • Default values, nulls, nullable values and other things to keep in mind:

So, I wanted to make a node that would return an actual value, and not a “single node function” if all of the inputs were not specified. That’s easy enough if we set up default values for our inputs using the attribute [DefaultArgument()], but in my case I wanted all of my defaults to be nulls. The question became how to set up a Zero Touch (ZT) node with default values of null. In most cases that’s easy enough as most objects are nullable (which means  you can declare Foo someObject = null), but things like integers require a special little thing. Have a look at this snippet of code to see how to make an integer nullable:

Now, you probably notices a question mark next to the integer. That pretty much tells the integer that it can store a null values. Sweet! But now, since integer can be null, and will be if default values are used, please remember that you have to cast the actual value to integer before being able to use it, or the compiler will bark at it.

You also probably noticed that I have this [DefaultArgument()] attribute used to define a default value that is null. Why? Well, I couldn’t just do int? projectionLineWeight = null, for my first input and then follow it with the [DefaultArgument()] for the next. Somehow, ZT doesn’t like that, and it forces you to push all inputs with default values set up using just a value declaration to the end, which in this case was not the order that I wanted for my inputs. It’s kinda funny, but I ended up creating a method that returns a null value so I could use [DefaultArgument()] syntax and order my inputs how I wanted them. It’s just a weird ZT thing to keep in mind.

Thanks to Dimitar Venkov, I have another tip for those of you that need to set up a default input for a List<object>. It looks like Dimitar found a way to do it just by simply declaring the [DefaultArgument(“{}”)]  with curly brackets. That’s pretty cool. Here’s a sample code:

  • Input descriptions, node description, output names, search values:

These are quite important, even though not critical to make things work smoothly when developing ZT nodes. One things that I always do, is rename my output port and you can do that quite easily just by adding a little bit of additional code to the XML summary: <returns name=”graphicsSettings”>Graphics Settings used by the View Filter Override.</returns> As you can see the output hover over description goes between the brackets, but to add a custom name to the output just add a name=”” tag. Now, what if you want to search for the node using keywords other than the name of the node itself just add another line to XML dressing: <search>graphics, settings, override</search> That was pretty straight forward. A typical input port name and description looks like this: <param name=”detailLevel”>Detail Level setting, ex: Coarse, Fine etc.</param> I don’t think that you can define a name for the input port that is different than a variable name, but I made that request with the Dynamo team already, and it might be possible someday in the future. The actual description for a node, that you see when hovering over the body of the node goes between the <summary></summary> tags. Here’s a full example for a one of the nodes:

Tip: if <returns name=”someName”>Some Description</returns> doesn’t contain the description bit between <returns></returns> tags, it will not work in Dynamo. You will see a return name assigned automatically as if you have not defined it.

  • Multiple outputs:

So this post isn’t exactly finished, but truth is that I haven’t yet had a need for multiple outputs node. I will try and thing about one that would require multiple outputs and post it here. It’s actually pretty easy and involves just making a Dictionary. I will explain that in great detail when I make one.

  • Debugging:

This is a bit of a pickle since if you are writing Revit components for Dynamo, you will need to be running Dynamo on top of Revit. This requirement means that you need to fire up Revit and then fire up Dynamo every single time you want to do some debugging. Yikes! That does take a little bit of time, so I hope everyone is running these Solid State drives and loads of patience.

  • Nested List input:

Matteo Cominetti brought this to my attention recently. The question was how to define an arbitrary list input (equivalent of setting input type to var[]…[] in Custom Nodes) but on a Zero Touch library in C#. The answer as it appears is to use an input attribute called [ArbitraryDimensionArrayImport]. Great stuff, Matteo! Thanks for sharing. Here’s a sample use from Dynamo GitHub page:

  • Make methods not load into Dynamo library:

Sometimes you might have a need to write a method or two that has to be public, and static. This means that by default Dynamo will try to interpret it as part of the library you are building and will attempt to expose it in the Dynamo Menu. That’s not always ideal, as we might have some utility methods  like the one that I was showing above and we want to hide it. All you have to do is just add this class property to it: [IsVisibleInDynamoLibrary(false)] Here’s the full code for it as I haven’t actually posted it above:

Ps. Maybe someone else wants to contribute to my blog and do a guest post explaining this or something else that they encountered with ZeroTouch and would like to share. Please let me know.

Support archi-lab on Patreon!

10 Comments

  1. Nicklas says:

    Is there a way to overide the node name?

  2. Nicklas says:

    Can I override inputs the same was as outputs like ///

  3. Jed says:

    What is the best way to convert an input to a simple Revit Element? In python we have the ‘UnwrapElement’ function, however I can’t find this in the c# api.

    For example, I want to be able to receive ceilings in dynamo as elements in revit.

    • try element.InternalElement

    • Jason says:

      I created an overloaded UnwrapElement function that I put in my top level zero touch classes that I use to convert the inputs, I used doc.GetElement as opposed to .InternalElement but that could easily be replaced:

      private static Autodesk.Revit.DB.Element UnwrapElement(Revit.Elements.Element dynEl)
      {
      Autodesk.Revit.DB.Document doc = RevitServices.Persistence.DocumentManager.Instance.CurrentDBDocument;
      Autodesk.Revit.DB.Element el = doc.GetElement(dynEl.UniqueId);
      return el;
      }
      private static List UnwrapElement(Revit.Elements.Element[] dynEls)
      {
      Autodesk.Revit.DB.Document doc = RevitServices.Persistence.DocumentManager.Instance.CurrentDBDocument;
      List outList = new List();

      foreach (Revit.Elements.Element dynEl in dynEls)
      {

      outList.Add(doc.GetElement(dynEl.UniqueId));
      }
      return outList;
      }

  4. Jed says:

    What is the best input type that can use .InternalElement? I’ve tried receiving a list of ceilings as both lists and arrays of Geometry and GeometryObject with no luck.

    Keep in mind I am trying to use this for ceiling elements, but also any other type of revit element where I can override the graphics. Thanks!

  5. Jason says:

    Hey @Konrad when it comes to debugging do you know if it;s possible to display the linenumber that’s throwing an exception? I’m having my nodes output the Exception message as a string but Traceback doesn’t seem to be producing a line number even though I do have a .pdb file in the same folder as my dll.

    • Jason says:

      Well never mind, I answered this myself and it was a silly mistake. Ended up just using Exception e.Traceback and make sure that you compile in debug and have the .pdb file in the same folder as the .dll.

Reply to Nicklas Cancel Reply