Melbourne Day One

These days right now represent a unique time in our life history.
Venus and Jupiter are in a

stunning conjunction
,
closer together right now than they will ever be again for the next two hundred years.
If you have a chance, don’t forget to look up at the sky in the evenings!

Venus Jupiter conjunction

In another unique and far-away event, at least from my normal habitat, we completed the first day of the Revit API Training here in Melbourne.

Talking about being far away from my everyday habitat, I went for a walk with my hosts Kim, Rob, Erika and Lewis and their friends Geoff, Vivienne and Alice up the Anakie Gorge in the

Brisbane Ranges National Park

last Saturday, enjoying the wonderful Australian flora:

Anakie Gorge

Back in the city and the Autodesk training room in

Queen’s Road
we
are a nice mix of participants, many with significant Revit product and some Revit API experience, others with zero of both, but decades of professional application development behind them and a wish to make use of it within the Revit API.

On the first day we went through the basics of the Revit API, all of which are also covered by the
materials provided by the
Revit Developer Center and
described in more detail in the

hands-on training preparation
suggestions.

Command Instantiation and Element Picking

One little sample command that we ended up developing together simply demonstrates interactive element selection and changing the name of an element type.
In the case of a wall, the attempt to change the name of the wall itself throws an exception, because the wall instance is actually just reflecting its type name.
For other instances it throws no exception but has no effect either.
Changing the code to modify the name of the element type instead of the instance works in both these cases.

While playing around with this, one interesting and previously unanswered question that we stumbled across was on the instantiation of the external command implementation class.

In AutoCAD.NET, a separate instance of a command implementation class is created for each document, and then reused for future command invocations in that document.
How is this handled in Revit?

The code required to explore and answer this question is very simple: just implement a public constructor for the class which counts and prints out the number of invocations.


  [Transaction( TransactionMode.Manual )]
 
  public class Command : IExternalCommand
  {
    static int _instance_count = 0;
 
    public Command()
    {
      Debug.Print( "{0} instances.",
        ++_instance_count );
    }
 
    // . . .
  }

As it turns out, a new instance of the command class is created every time the command is launched.

Oops.
Looking in a bit more depth, we discovered that this is in fact clearly stated in the developer guide, which says: “When a command is selected, a command object is created and its Execute() method is called. Once this method returns back to Revit, the command object is destroyed.”

This confirms what every careful programmer would do subconsciously anyway: keep your command class implementation as light as possible, since it will be re-instantiated for each call of your command.

If you need to store any large amounts of data, then do so either in static variables, or, more cleanly, in separate singleton classes elsewhere.

The code we ended up with to select an element and rename its type looks like this:


public Result Execute(
  ExternalCommandData commandData,
  ref string message,
  ElementSet elements )
{
  UIApplication uiapp = commandData.Application;
  UIDocument uidoc = uiapp.ActiveUIDocument;
  Application app = uiapp.Application;
  Document doc = uidoc.Document;
 
  Element e;
 
  try
  {
    Reference r = uidoc.Selection.PickObject(
      ObjectType.Element,
      "Please pick an element." );
 
    e = doc.get_Element( r.ElementId );
  }
  catch( RvtOperationCanceledException )
  {
    return Result.Cancelled;
  }
 
  using( Transaction tx = new Transaction( doc ) )
  {
    tx.Start( "Rename Element" );
 
    ElementId id = e.GetTypeId();
 
    Element type = doc.get_Element( id );
 
    if( null != type )
    {
      type.Name = "Melbourne " + type.Name;
    }
 
    tx.Commit();
  }
  return Result.Succeeded;
}

We used this to discuss a number of basic aspects of add-in creation:

  • Referencing the Revit API assemblies and

    setting the copy local flag
    .
  • Implementing the basic application skeleton code.
  • Using attributes to define the journaling, regeneration and transaction options.
    Journaling we might return to tomorrow, regeneration is trivial, since there is only one single option nowadays, and transaction is important to understand: automatic, manual or read-only, of which I generally recommend using only the latter two.
  • Creating the

    add-in manifest and GUID
    and

    other add-in manifest features
    .
  • Using the

    Revit add-in wizard
    to
    handle all that automatically.
  • Selecting an element using PickObject and handling the exception thrown by user cancellation.
  • Transaction management and element modification.
  • Instances versus types.

Filtered Element Collector and Using Parameter Filter for Non-Empty String

In a second step, we had a look at a filtered element collector to access the Revit database contents.

We decided that parameter filters are of special interest, and explored how to filter for an empty and a non-empty string value.

Here is the code that we ended up with:


public Result Execute(
  ExternalCommandData commandData,
  ref string message,
  ElementSet elements )
{
  UIApplication uiapp = commandData.Application;
  UIDocument uidoc = uiapp.ActiveUIDocument;
  Application app = uiapp.Application;
  Document doc = uidoc.Document;
 
  BuiltInParameter bip
    = BuiltInParameter.ALL_MODEL_MARK;
 
  ParameterValueProvider provider
    = new ParameterValueProvider(
      new ElementId( bip ) );
 
  // Filter for an empty string:
 
  //FilterStringRuleEvaluator evaluator 
  //  = new FilterStringEquals();
 
  // Filter for an non-empty string:
 
  FilterStringRuleEvaluator evaluator
    = new FilterStringGreater();
 
  FilterStringRule rule = new FilterStringRule(
    provider, evaluator, "", false );
 
  bool inverted = false;
 
  ElementParameterFilter filter
    = new ElementParameterFilter( rule, inverted );
 
  FilteredElementCollector col
    = new FilteredElementCollector( doc )
      .WhereElementIsNotElementType()
      .WherePasses( filter );
 
  foreach( Element e in col )
  {
    Parameter p = e.get_Parameter( bip );
 
    Debug.Print( "'{0}': '{1}'",
      e.Name,
      (null==p? "null" : p.AsString() ) );
  }
 
  return Result.Succeeded;
}

In its current, final state, it uses a parameter string filter to retrieve and list all elements with a non-empty Mark parameter value.

To do so, we search for any string values greater than the empty string “”.

We also tried using a null value instead of the empty string, but that throws a rather inelegant exception in the FilterStringRule constructor saying

  • ArgumentNullException:
    “The input argument “ruleString” of function
    `anonymous-namespace’::FilterStringRule_constructor
    or one item in the collection is null at line 1193
    of file n:\build\2012_ship_inst_20110916_2132
    \source\api\revitapi\gensrc\APIFilterRule.cpp.
    rnParameter name: ruleString”

We also tested searching for all empty string values using a FilterStringEquals evaluator, and that worked fine as well.

Evernote and a Revit Product and Family Tutorial

During our explorations, we underlined the importance of families and the family API.
I mentioned the Autodesk

Revit 2010 Families Guide


several


times


in the


past
.
It is free and covers the basics well together with Google, especially some important Revit MEP content best practices.
<!–
The Seek Family Guidelines
and am sorry to say that it is not highly regarded and advises several non-optimal practices in this complex area.
I think one of the terms used was ‘absolute rubbish’.
199_family_api.htm:

  • Families Guide
    600_instance_void_cut.htm:Families Guide (still the
    664_families_guide.htm:

    Families Guide

    664_families_guide.htm:Revit Families Guide.
    723_spark_learning.htm:Revit Families Guide is
    –>

    For more background, especially on Revit MEP, the

    Learning Autodesk Revit MEP 2012
    video
    training by Simon Whitbread,
    <!– of KarelCAD –>
    Don Bokmiller and Joel Londenberg is recommended.

    One neat little non-Revit-API topic that popped up was the handy and free little

    Evernote
    utility
    for storing and sharing notes across the cloud and various mobile devices.


  • Comments

    Leave a Reply

    Discover more from Autodesk Developer Blog

    Subscribe now to keep reading and get access to the full archive.

    Continue reading